I have the following C++11 code (simplified version):
struct Info
{
const char * name;
int version;
};
class Base
{
public:
const Info info;
Base (Info info) : info (info) {}
};
class Derived : public Base
{
public:
static constexpr Info info = {"Foobar", 2};
Derived () : Base (info) {}
};
int main ()
{
static Derived derived;
return 0;
}
GCC 4.9.1 compiles and links this code fine. Clang 3.5.0, on the other hand, complains about an undefined reference:
/tmp/test-109c5c.o: In function `main':
test.cc:(.text+0x1c): undefined reference to `Derived::info'
test.cc:(.text+0x22): undefined reference to `Derived::info'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Which is right? Is this code legal or not? My understanding of the rules regarding static constexpr members (based mostly on this question) is that an out-of-class definition is needed only when the address of the variable is taken. But I'm not taking the address of Derived::info or using a reference to it anywhere; I'm only passing it by value to the Base constructor.
Various workarounds that I've found:
Base ({"Foobar", 2})
. This solution would work, but it gets ugly (in my opinion) as more members are added to struct Info.A static constexpr variable has to be set at compilation, because its lifetime is the the whole program. Without the static keyword, the compiler isn't bound to set the value at compilation, and could decide to set it later. So, what does constexpr mean?
The alternatives don't have the all of the positives of static constexpr - you're guaranteed compile time processing, type safety, and (potentially) lower usage of memory (constexpr variables don't need to take up memory, they are effectively hard coded unless if possible).
If you need to take the address of constexpr variable, declare it as a static member variable. It can be used as a constant expression this way (as opposed to using a function returning a const).
Aha, it seems that the problem is the implicit Info(const Info &)
copy constructor. To pass the const Info &
reference to that constructor, it's necessary to take the address of Derived::info.
Apparently GCC is more aggressive than Clang in optimizing away the copy constructor. If I use -fno-elide-constructors
, then GCC also complains about an undefined reference to Derived::info.
In any case, declaring the Base and Derived constructors as constexpr seems to accomplish what I want to happen here, which is to have Base::info initialized at compile time, rather than copied from a separate Derived::info at run time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With