8

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:

  • Make both constructors (Base and Derived) constexpr. This may or may not be an option with the real classes, which are more complex than the ones in the example. I'm going to try it, anyway.
  • Declare the instance of Derived in main with automatic rather than static duration. This is not an option for the real project: the Derived class is a plugin implementation, and an instance of it needs to be exported as a public symbol in a shared object.
  • Remove Derived::info entirely and call the Base constructor with a brace-initialized temporary object instead, i.e. Base ({"Foobar", 2}). This solution would work, but it gets ugly (in my opinion) as more members are added to struct Info.
Community
  • 1
  • 1
John Lindgren
  • 777
  • 5
  • 14
  • 1
    Possible duplicate of [vector::push_back odr-uses the value, causing undefined reference to static class member](http://stackoverflow.com/questions/272900/undefined-reference-to-static-class-member) – πάντα ῥεῖ Sep 27 '14 at 18:20
  • What about of making it a member function instead of data member? – imreal Sep 27 '14 at 18:41
  • @πάνταῥεῖ I'm not using std::vector, but your link put me on the right track. Thanks! – John Lindgren Sep 27 '14 at 19:25
  • @imreal That would be another workaround. I ended up going with the solution of making the constructors constexpr, since that eliminates run-time copying altogether (the reason I was using constexpr in the first place). – John Lindgren Sep 27 '14 at 19:35

1 Answers1

2

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.

John Lindgren
  • 777
  • 5
  • 14