Static Constexpr Member of Same Type as Class Being Defined

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 of
Size::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



Leave a reply



Submit