Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does constexpr static member (of type class) require a definition?

==> See the full snippet code and compilation on coliru.

I have a LiteralType class filling constexpr requirements:

struct MyString
{
    constexpr MyString(char const* p, int s) : ptr(p), sz(s) {}
    constexpr char const* data() const { return ptr; }
    constexpr int         size() const { return sz;  }

    char const *ptr = 0;
    int  const  sz  = 0;
};

I use it as a constexpr static member variable:

struct Foo
{
    int size() { return str_.size(); }

    constexpr static MyString str_{"ABC",3};
};

int main()
{
  Foo foo;
  return ! foo.size();
}

But the linker says:
(Clang-3.5 and GCC-4.9)

undefined reference to `Foo::str_'

I have to define the constexpr static member!
(I do not specify the constructor parameters)

constexpr MyString Foo::str_;

However if the constexpr static member had been an int the member would not have to be defined outside the class definition. This is my understanding, but I am not sure...

Questions:

  • Why int does not need to be defined outside the class declaration but MyString requires this?
  • Is there a disadvantage to define a constexpr static member in a header file?
    (I provide my library as header files only)
like image 943
oHo Avatar asked Apr 01 '15 18:04

oHo


1 Answers

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.

like image 90
Shafik Yaghmour Avatar answered Oct 17 '22 05:10

Shafik Yaghmour