57

I am compiling the following simple program with g++-4.6.1 --std=c++0x:

#include <algorithm>

struct S
{
    static constexpr int X = 10;
};

int main()
{
    return std::min(S::X, 0);
};

I get the following linker error:

/tmp/ccBj7UBt.o: In function `main':
scratch.cpp:(.text+0x17): undefined reference to `S::X'
collect2: ld returned 1 exit status

I realize that inline-defined static members do not have symbols defined, but I was under the (probably flawed) impression that using constexpr told the compiler to always treat the symbol as an expression; so, the compiler would know that it is not legal to pass a reference to the symbol S::X (for the same reason you can't take a reference to the literal 10).

However if S is declared as namespace, i.e. "namespace S" instead of "struct S", everything links fine.

Is this a g++ bug or do I still have to use a trick to workaround this annoyance?

Community
  • 1
  • 1
Travis Gockel
  • 26,877
  • 14
  • 89
  • 116

6 Answers6

39

I don't think this is a bug. If you change the constexpr to const, it still fails, with the exact same error.

You've declared S::X, but not defined it anywhere, so there's no storage for it. If you do anything with it that needs to know the address of it then you'll need to define it somewhere also.

Examples:

int main() {
      int i = S::X; // fine
      foo<S::X>(); // fine
      const int *p = &S::X; // needs definition
      return std::min(S::X, 0); // needs it also
}

The reason for this is that constexpr can be evaluated at compile time, but it's not required to be evaluated as such, and can equally happen at runtime. It doesn't instruct "the compiler to always treat the symbol as an expression", it hints that it would be sensible and permissible to do so if the compiler felt like it.

Flexo
  • 87,323
  • 22
  • 191
  • 272
  • 24
    You only need a definition of `S::X` because `std::min` takes its argument by reference. If it took its argument by value, then no definition would be required, because, by [basic.def.odr]p2, a definition of variable which is mentioned in a potentially-evaluated context is required "unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied", which it would be in this case. – Richard Smith Dec 30 '11 at 20:53
  • So, why is it a difference if it is a static struct variable or just a global variable? – Albert Jun 23 '14 at 13:49
  • @albert not totally sure on the circumstances you're considering, but most likely the behaviour you're seeing is influenced by the optimisations the compiler is making too. Either that or you've written something that is both a declaration and a definition simultaneously. – Flexo Jun 24 '14 at 00:34
  • @Flexo: Exactly like in the original question. If it is `namespace S`, there is no linker error, if it is `struct S`, there is one. – Albert Jun 24 '14 at 07:10
  • Also, in the [standard](http://www.open-std.org/Jtc1/sc22/wg21/docs/papers/2011/n3242.pdf), it says "A name having namespace scope (3.3.6) has internal linkage if it is the name of [...] a variable that is explicitly declared const or constexpr ...". From what I understand, this is different for a static name in struct scope. Thus, in the one case, the symbol must be defined, in the other case, it must not. This is not influenced by the compiler optimizations. – Albert Jun 24 '14 at 08:04
  • It's perhaps worth pointing out that inside `constexpr` evaluation it is perfectly fine to call `std::min` with `static constexpr` arguments without definitions, it only breaks if `std::min` is called at runtime (and probably only in debug builds which don't optimize the call away). In other words, this feature is really broken in C++14. Basically, header-only libraries cannot use it at all without risking breakage. – tobi_s Mar 19 '22 at 01:49
16

This has been fixed in C++17.

https://en.cppreference.com/w/cpp/language/static:

If a static data member is declared constexpr, it is implicitly inline and does not need to be redeclared at namespace scope. This redeclaration without an initializer (formerly required as shown above) is still permitted, but is deprecated.

Trass3r
  • 5,858
  • 2
  • 30
  • 45
14

The reason for the error has been already explained, so I'd just add a workaround.

return std::min(int(S::X), 0);

This creates a temporary, so std::min could take a reference to it.

VladV
  • 10,093
  • 3
  • 32
  • 48
12

You also need to provide a definition for the constexpr member outside the struct (or class), but this time without its value. See here: https://en.cppreference.com/w/cpp/language/static

#include <algorithm>

struct S
{
    static constexpr int X = 10;
};

constexpr int S::X;

int main()
{
    return std::min(S::X, 0);
};
jciloa
  • 1,039
  • 1
  • 11
  • 22
5

In the C++ standard (latest working draft), it says:

A name having namespace scope (3.3.6) has internal linkage if it is the name of [...] a variable that is explicitly declared const or constexpr and neither explicitly declared extern nor previously declared to have external linkage [...].

"Linkage" is defined like this:

A name is said to have linkage when it might denote the same object, reference, function, type, template, namespace or value as a name introduced by a declaration in another scope:

— When a name has external linkage, the entity it denotes can be referred to by names from scopes of other translation units or from other scopes of the same translation unit.

— When a name has internal linkage, the entity it denotes can be referred to by names from other scopes in the same translation unit.

— When a name has no linkage, the entity it denotes cannot be referred to by names from other scopes.

Thus, in case of namespace S, it will have external linkage, in case of struct S, it will have internal linkage.

Symbols with external linkage need to have the symbol defined explicitly in some translation unit.

Albert
  • 65,406
  • 61
  • 242
  • 386
  • Linkage is not related to this question. The declaration of `X` inside `struct S` is **not** a definition, while inside `namespace S` it is actually a definition. The function `min` takes its argument by reference and hence a definition is required. – MWid Jun 24 '14 at 11:41
0

Your understanding of constexpr is wrong. An lvalue declared constexpr is still an lvalue, and a function declared constexpr is still a function. And when a function has a reference parameter, and it is passed an lvalue, the language requires that the reference refer to that lvalue, and nothing else. (When applied to a variable of type int, there is really very little difference between constexpr and plain const.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329