静态constexpr类成员什么时候需要一个类外定义?

Joh*_*ren 8 c++ plugins constexpr c++11

我有以下C++ 11代码(简化版):

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;
}
Run Code Online (Sandbox Code Playgroud)

GCC 4.9.1编译并链接此代码.另一方面,Clang 3.5.0抱怨未定义的引用:

/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)
Run Code Online (Sandbox Code Playgroud)

哪个是对的?这段代码是否合法?我对静态constexpr成员(主要基于这个问题)的规则的理解是,只有在获取变量的地址时才需要类外定义.但是我没有使用Derived :: info的地址或者在任何地方使用它的引用; 我只是通过值传递给Base构造函数.

我发现了各种解决方法:

  • 使两个构造函数(Base和Derived)constexpr.这可能是也可能不是真实类的选项,它们比示例中的类更复杂.无论如何,我打算尝试一下.
  • 使用自动而非静态持续时间在main中声明Derived的实例.这不是真实项目的选项:Derived类是一个插件实现,需要将其实例导出为共享对象中的公共符号.
  • 完全删除Derived :: info并使用大括号初始化的临时对象调用Base构造函数,即Base ({"Foobar", 2}).这个解决方案可行,但随着更多成员被添加到struct Info中,它会变得很丑陋(在我看来).

Joh*_*ren 2

啊哈,看来问题出在隐式Info(const Info &)复制构造函数上。要将引用const Info &传递给该构造函数,需要获取 Derived::info 的地址。

显然,GCC 在优化复制构造函数方面比 Clang 更积极。如果我使用-fno-elide-constructors,那么 GCC 还会抱怨对 Derived::info 的未定义引用。

无论如何,将 Base 和 Derived 构造函数声明为 constexpr 似乎完成了我想要在这里发生的事情,即在编译时初始化 Base::info,而不是在运行时从单独的 Derived::info 复制。