R Sahu
R Sahu

Reputation: 206717

Strange behavior with constexpr static member variable

This is a follow up question to Undefined reference to static constexpr char[][].

The following program builds and runs fine.

#include <iostream>

struct A {
   constexpr static char dict[] = "test";

   void print() {
      std::cout << A::dict[0] << std::endl;
   }
};

int main() {
   A a;
   a.print();
   return 0;
}

However, if I change A::print() to:

   void print() {
      std::cout << A::dict << std::endl;
   }

I get the following linker error in g++ 4.8.2.

/tmp/cczmF84A.o: In function `A::print()':
socc.cc:(.text._ZN1A5printEv[_ZN1A5printEv]+0xd): undefined reference to `A::dict'
collect2: error: ld returned 1 exit status

The linker error can be resolved by adding a line:

constexpr char A::dict[];

outside the class definition.

However, it's not clear to me why using one of the members of the array does not cause a linker error while using the array causes a linker error.

Upvotes: 11

Views: 479

Answers (2)

R Sahu
R Sahu

Reputation: 206717

In addition to the information provided by @hvd in his answer...

From the C++ Draft Standard N3337 (emphasis mine):

9.4.2 Static data members

3 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. [ Note: In both these cases, the member may appear in constant expressions. — end note ] 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.

Given that A::data is odr-used in the expression A::data[0], as per the standard, it shall be defined in a namespace scope. The fact that g++ is able to successfully create a program without A::data being defined in a namescpace scope does not make the program right. To be standards compliant, A::data shall be defined.

Upvotes: 0

user743382
user743382

Reputation:

The standard does not require any diagnostics for a failure to provide a definition where one is required.

3.2 One definition rule [basic.def.odr]

4 Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required. [...]

This means implementations are allowed to optimise away accesses to such variables, and that's what's happening in your first case with GCC.

Both GCC and clang have decided that they prefer a consistent user experience, where error messages about missing definitions do not depend on the optimisation level. Usually, that means that any missing definition causes an error message. However, in this case, GCC is doing some minimal optimisation even at -O0, avoiding the error.

But the program is an error either way, because even A::dict[0] is an ODR-use:

3.2 One definition rule [basic.def.odr]

3 A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex 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). [...]

The use of A::dict doesn't involve lvalue-to-rvalue conversion, it involves the array-to-pointer conversion, so the exception doesn't apply.

Upvotes: 7

Related Questions