static constexpr member of same type as class being defined
If I interpret the Standard correctly, it isn't possible.
(§9.4.2/3) [...] A static data member of literal type can be declared in the
class definition with the constexpr 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. [...]
From the above (along with the fact that there is no separate statement about non-literal types in static data member declarations), I believe it follows that a static data member that is constexpr
must be a literal type (as defined in §3.9/10), and it must have its definition included in the declaration. The latter condition could be satisfied by using the following code:
struct Foo {
constexpr Foo() {}
static constexpr Foo f {};
};
which is similar to your Attempt 1, but without the class-external definition.
However, since Foo
is incomplete at the time of declaration/definition of the static member, the compiler can't check whether it is a literal type (as defined in §3.9/10), so it rejects the code.
Note that there is this post-C++-11 document (N3308) which discusses various problems of the current definition of constexpr
in the Standard, and makes suggestions for amendments. Specifically, the "Proposed Wording" section suggests an amendment of §3.9/10 that implies the inclusion of incomplete types as one kind of literal type. If that amendment was to be accepted into a future version of the Standard, your problem would be solved.
Define a static constexpr member of same type of a template class
I think GCC is correct in accepting the given example. This is because the static
data member named constant
is an ordinary data member variable(although it is still considered a templated entity).
And since constant
is an ordinary data member variable, dcl.constexpr#1.sentence-1 is applicable to it:
The constexpr specifier shall be applied only to the definition of a variable or variable template or the declaration of a function or function template.
(emphasis mine)
Thus, as the construct that you have provided after the class template is nothing but an out-of-class definition for the ordinary static data member constant
, the given example is well-formed.
template <typename T>
constexpr Test<T> Test<T>::constant {42}; //this is an out-of-class definition for the ordinary static data member variable `constant`
Note
Note that dcl.constexpr#1.sentence-1 doesn't mention templated entity, but instead only mention "variable" and "variable template" and since constant
is a variable(ordinary), the same should be applicable to constant
.
Can't a class have static constexpr member instances of itself?
Is there a way to achieve the same result?
By "the same result", do you specifically intend the constexpr
-ness ofSize::big
and Size::small
? In that case maybe this would be close enough:
struct Size
{
const unsigned int width = 0;
const unsigned int height = 0;
static constexpr Size big() {
return Size { 480, 240 };
}
static constexpr Size small() {
return Size { 210, 170 };
}
private:
constexpr Size() = default;
constexpr Size(int w, int h )
: width(w),height(h){}
};
static_assert(Size::big().width == 480,"");
static_assert(Size::small().height == 170,"");
Static constexpr members of same type as class defined (additional details)
As @dyp mentioned the solution for one
compiles because function definitions are compiled after the class body. So it is like one
has been declared like this
class MyEnum
{
public:
static constexpr const MyEnum one();
//... Definition here
}; //Class is fully defined after here
inline static constexpr const MyEnum MyEnum::one() { return MyEnum(1); }
//Fine here because class is complete ^^^^
On the other hand, definitions in the class body are compiled as they are placed in the class body. So when two
and three
are being compiled the class is not fully defined yet.
Can I initialize a `constexpr static` member outside the class?
constexpr
goes on the initializing declaration of a variable, so just put it outside the class:
struct Header
{
int msgType = -1, len;
static const std::size_t MAX_SIZE;
Header() { len = sizeof(*this); }
};
// ... declaration of subclasses ...
inline constexpr std::size_t Header::MAX_SIZE = std::max({ sizeof(A), sizeof(B), sizeof(C) });
Note that the implicit const
must be spelled out in the declaration. The definition should go in the same header to avoid any translation unit seeing the declaration but not the inline
, which is not allowed.
Why does constexpr static member (of type class) require a definition?
The One Definition rule tells us that we can not have more than one definition of an odr-used variable in a program. So if a variable is odr-used then you need to define it but you can not define it the header file since it may be included more than once with the whole program. Odr-use violations do not require a diagnostic message and so you can violate this rule and the compiler is not obliged to notify you.
In your case you are indeed odr-using str_
, and you can not include the definition in the header file because that would violate the one definiton rule since it can be included more than once within the program.
It is interesting to note that if you had done the following it would not have been odr-used:
return str_.size_;
You would therefore not need to define the variable, which can have some odd consequences in some examples. I doubt that really solves your problem long-term.
The odr rules are covered in the draft C++ standard section 3.2
and they say:
A variable x whose name appears as a potentially-evaluated expression
ex is odr-used unless applying the lvalue-to-rvalue conversion (4.1)
to x yields a constant expression (5.19) that does not invoke any
non-trivial functions and, if x is an object, ex is an element of the
set of potential results of an expression e, where either the
lvalue-to-rvalue conversion (4.1) is applied to e, or e is a
discarded-value expression (Clause 5). this is odr-used if it appears
as a potentially-evaluated expression (including as the result of the
implicit transformation in the body of a non-static member function
(9.3.1)).[...]
So str_
yield a constant expression, the lvalue-to-rvalue conversion is not applied the expression str_.size()
and it is not a discarded value expression, so it is odr-used and therefore str_
is required to be defined.
On the other hand the lvalue-to-rvalue conversion is applied to the expression str_.size_
, so it is not odr-used and does not require str_
to be defined.
Related Topics
Cout or Printf Which of the Two Has a Faster Execution Speed C++
Changing the Value of Const Variable in C++
In C++ , What's So Special About "_Move_H"
How to Append an Int to a String in C++
Mingw .Exe Requires a Few Gcc Dll's Regardless of the Code
Lambda Implicit Capture Fails with Variable Declared from Structured Binding
Tell Gdb to Skip Standard Files
More Elegant Way to Check for Duplicates in C++ Array
Child Process Receives Parent's Sigint
C++ String to Double Conversion
How to Pass a Template Function in a Template Argument List
How to Use Openssl's Sha256 Functions
Cast Pointer to Member Function to Normal Pointer
C++ HTML Template Framework, Templatizing Library, HTML Generator Library
Linking Different Libraries for Debug and Release Builds in Cmake on Windows
Why Is "Using Namespace X;" Not Allowed at Class/Struct Level