I'm trying to compile this code, but g++ complains about ZERO
having an incomplete type. Does this mean that in C++ a struct cannot contain a static constexpr
instance of itself? If so, why?
struct Cursor
{
size_t row,column;
static constexpr Cursor ZERO {0,0};
//error: constexpr const Cursor Cursor::ZERO has incomplete type
};
EDIT: I understand that Cursor
cannot have a complete type when I declare ZERO
. What I'd like to know is: is there any way I can have ZERO
belonging to Cursor
and still being constexpr
?
Unfortunately, you simply cannot do this!
Some static constexpr
members may be initialised inline:
[C++11 9.4.2/3]:
[..] Astatic
data member of literal type can be declared in the class definition with theconstexpr
specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [..]
Cursor
is a literal type, so this counts.
And the use of Cursor
itself as a static
data member within its own type is not a problem, as long as you initialise it at lexical namespace scope:
[C++11: 9.4.2/2]:
The declaration of astatic
data member in its class definition is not a definition and may be of an incomplete type other than cv-qualified void. The definition for astatic
data member shall appear in a namespace scope enclosing the member's class definition. In the definition at namespace scope, the name of thestatic
data member shall be qualified by its class name using the::
operator. The initializer expression in the definition of astatic
data member is in the scope of its class (3.3.7).
But you can't do that with constexpr
:
[C++11: 7.1.5/9]:
Aconstexpr
specifier used in an object declaration declares the object asconst
. Such an object shall have literal type and shall be initialized. [..]
I think all of this wording could be improved but, in the meantime, I think you're going to have to make ZERO
a non-member in the enclosing namespace.
is there any way I can have ZERO belonging to Cursor and still being
constexpr
?
Yes, if you count nested subclasses as "belonging to" the containing class:
struct Cursor
{
size_t row,column;
struct Constants;
};
struct Cursor::Constants
{
static constexpr Cursor ZERO {0,0};
};
This answer to a similar question revealed that this is, in fact, possible to accomplish. You just have to put the constexpr
keyword with the definition rather than the declaration :
#include <iostream>
#include <array>
struct Cursor
{
static Cursor const ZERO;
std::size_t row, column;
};
constexpr Cursor const Cursor::ZERO{ 0, 0 };
int main(int, char**) noexcept
{
// using the values in a template argument ensure compile-time usage
std::array<int, Cursor::ZERO.row> row_arr{};
std::array<int, Cursor::ZERO.column> col_arr{};
std::cout << "rows: " << row_arr.size() << "\ncols: " << col_arr.size();
return 0;
}
Fully tested with GCC ( ideone ), clang ( rextester ), and MSVC++ 2017 (note that IntelliSense doesn't like it, but it compiles correctly!).
You can if you accept to have a function, not a variable
struct Cursor
{
size_t row,column;
static constexpr Cursor ZERO() { return Cursor{0,0}; }
};
A bit late, but here's a workaround if all you need is a constant:
struct Cursor
{
struct CursorInit
{
int a, b;
constexpr operator Cursor() const;
};
int row, column;
static constexpr CursorInit ZERO {0,0};
constexpr bool operator==(const Cursor& rhs) const
{
return row == rhs.row && column == rhs.column;
}
};
inline constexpr Cursor::CursorInit::operator Cursor() const
{
return Cursor{a, b};
}
static_assert(Cursor::ZERO == Cursor{0, 0});
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.