11

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.

Community
  • 1
  • 1
R Sahu
  • 204,454
  • 14
  • 159
  • 270

2 Answers2

7

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.

Community
  • 1
  • 1
  • 2
    This might benefit from a discussion about why `A::dict[0]` is an odr-use. – T.C. Feb 16 '15 at 05:24
  • @T.C. It isn't anymore. – Columbo Jan 14 '16 at 15:46
  • @Columbo Thanks for the update. I was about to edit my answer, but then noticed the question is tagged C++11, so the information is still correct. :) –  Jan 15 '16 at 06:44
0

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.

Community
  • 1
  • 1
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Huh, you're right, there's that too. That *doesn't* say "no diagnostic required", so you might even legitimately argue that GCC is buggy for failing to diagnose the violation of that rule. (But more likely is that this is a nit in the wording, and a diagnostic isn't meant to be required.) –  Feb 16 '15 at 06:06
  • @hvd, I think "no diagnostic required" still applies since it references "odr-used (3.2)", – R Sahu Feb 16 '15 at 06:08
  • Yeah, but odr-used just refers to [3.2]p3, which specifies which variables are odr-used, not to [3.2]p4, which requires definitions for those variables that are odr-used. [3.2]p3 is where "odr-used" appears in italics, and this appearance in italics means that it defines what the term "odr-used" means. –  Feb 16 '15 at 06:11