Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Linker Error With Class static constexpr

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?

like image 352
Travis Gockel Avatar asked Dec 09 '11 23:12

Travis Gockel


People also ask

Is it possible to redeclare static data members in C++17?

This has been fixed in C++17. 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.

Why constexpr is not evaluated as an expression at runtime?

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.

Is it common to write static const members like this?

In legacy code, it is very commonfor people to write static constmembers like this, and then simply never write the connection.cpppart. As long as you never pass DefaultTimeoutMsby reference, you’ll never notice the problem.

What's wrong with static const variables of integral type?

The weird thing about static constmember variables of integral type is that you are allowed to move their initializing expression from the definition to the declaration!


2 Answers

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.

like image 135
Flexo Avatar answered Oct 09 '22 14:10

Flexo


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.

like image 42
Trass3r Avatar answered Oct 09 '22 13:10

Trass3r