静态constexpr类成员何时需要类外定义? [英] When does a static constexpr class member need an out-of-class definition?

查看:204
本文介绍了静态constexpr类成员何时需要类外定义?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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可以很好地编译和链接此代码.另一方面,Clang 3.5.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)

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

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:

  • 使两个构造函数(基本和派生)都为constexpr.对于真实类,这可能是选项,也可能不是,这比示例中的复杂.无论如何,我都会尝试的.
  • 使用自动而不是静态持续时间声明main中的Derived实例.对于真实的项目,这不是一个选择:Derived类是一个插件实现,它的一个实例需要作为公共符号导出到共享对象中.
  • 完全删除Derived :: info并使用括号初始化的临时对象(即Base ({"Foobar", 2}))调用Base构造函数.该解决方案可以工作,但是(在我看来)随着更多的成员添加到结构信息中而变得很难看.
  • 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.

推荐答案

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

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.

显然,在优化复制构造函数方面,GCC比Clang更具攻击性.如果我使用-fno-elide-constructors,则GCC还会抱怨未定义对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.

无论如何,将Base和Derived构造函数声明为constexpr似乎可以完成我想在这里完成的工作,即在编译时初始化Base :: info,而不是在运行时从单独的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.

这篇关于静态constexpr类成员何时需要类外定义?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆