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?
This has been fixed in C++17.
https://en.cppreference.com/w/cpp/language/static:
I don't think this is a bug. If you change the
constexpr
toconst
, 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:
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.In the C++ standard (latest working draft), it says:
"Linkage" is defined like this:
Thus, in case of
namespace S
, it will have external linkage, in case ofstruct S
, it will have internal linkage.Symbols with external linkage need to have the symbol defined explicitly in some translation unit.
The reason for the error has been already explained, so I'd just add a workaround.
This creates a temporary, so
std::min
could take a reference to it.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
Your understanding of
constexpr
is wrong. An lvalue declaredconstexpr
is still an lvalue, and a function declaredconstexpr
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 typeint
, there is really very little difference betweenconstexpr
and plainconst
.)