Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper initialization of static constexpr array in class template?

Static class members in C++ have caused a little confusion for me due to the standard's verbiage:

9.4.2 Static data members [class.static.data]

The declaration of a static data member in its class definition is not a definition...

However a constexpr is required to be initialized (AFAIK, couldn't find a quote from the standard) at its declaration (e.g., in the class definition).

Because of the restrictions on constexpr I had actually forgotten about the requisite for static members to be defined outside of the class, until I tried accessing a static constexpr array. This related question provides the correct way of defining the array member, but I'm interested as to the implications on this definition in a class template.

This is what I ended up with:

template<typename T>
class MyClass
{
private:
  static constexpr std::size_t _lut[256] = { /* ... */ };
  T _data;

public:
  static constexpr std::size_t GetValue(std::size_t n) noexcept
  {
    return _lut[n & 255];
  }

  // ...
};

template<typename T>
constexpr std::size_t MyClass<T>::_lut[256];

Is this the right syntax? Particularly the use of template in the definition feels awkward, but GCC seems to be linking everything appropriately.

As a follow-up question, should non-array static constexpr members be similarly defined (with template definition outside of class)?

like image 699
monkey0506 Avatar asked Jan 18 '13 09:01

monkey0506


People also ask

How to initialize constexpr array?

Manual initialization A constexpr array can be declared and initialized manually through: constexpr int arr[3] = {1,2,3};

How to declare a constexpr in c++?

A reference may be declared as constexpr when both these conditions are met: The referenced object is initialized by a constant expression, and any implicit conversions invoked during initialization are also constant expressions. All declarations of a constexpr variable or function must have the constexpr specifier.

Are constexpr functions static?

It may contain local variable declarations, but the variable must be initialized. It must be a literal type, and can't be static or thread-local. The locally declared variable isn't required to be const , and may mutate. A constexpr non- static member function isn't required to be implicitly const .

What is a constexpr function?

A constexpr function is a function that can be invoked within a constant expression. A constexpr function must satisfy the following conditions: It is not virtual. Its return type is a literal type. Each of its parameters must be of a literal type.


2 Answers

In case it helps anyone out, the following worked for me with GCC 4.7 using constexpr:

template<class T> class X {
  constexpr static int s = 0;
};
template<class T> constexpr int X<T>::s; // link error if this line is omitted

I'm not making any claims of whether this is "proper". I'll leave that to those more qualified.

like image 193
arr_sea Avatar answered Oct 04 '22 16:10

arr_sea


I think you want 9.4.2p3:

If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression (5.19). 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. [...] The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not contain an initializer.

The definition of a template static data member is a template-declaration (14p1). The example given in 14.5.1.3p1 is:

template<class T> class X {
  static T s;
};
template<class T> T X<T>::s = 0;

However, as above a constexpr static or const static member whose in-class declaration specifies an initializer should not have an initializer in its namespace scope definition, so the syntax becomes:

template<class T> class X {
  const static T s = 0;
};
template<class T> T X<T>::s;

The difference with the non-array (i.e. integral or enumeration) static constexpr data member is that its use in lvalue-to-rvalue conversion is not odr-use; you would only need to define it if taking its address or forming a const reference to it.

like image 42
ecatmur Avatar answered Oct 04 '22 16:10

ecatmur