创建非odr使用的文字类型

时间:2016-04-05 18:42:27

标签: c++ c++14

请考虑以下类型定义:

struct lit
{
    enum { A, B } value;
    constexpr lit() : value(A) { }
    constexpr lit(int) : value(B) { }
};

根据C ++ 14标准(3.9 / 10),此类型符合文字类型。接下来,请考虑以下使用方案:

struct foo
{
    static constexpr lit a { };
    static constexpr lit b { 10 };
    static constexpr int i { 42 };
};

int main()
{
    lit a = foo::a;
    lit b = foo::b;
    int i = foo::i;

    std::cout << "a=" << a.value << std::endl;
    std::cout << "b=" << b.value << std::endl;
    std::cout << "i=" << i       << std::endl;
    return 0;
}

该标准的第9.4.2 / 3节虽然没有直接说明,但暗示我不必指定static constexpr成员的定义,除非他们使用的是odr-used (例如,考虑foo::i)。

当我用clang编译上面的内容时,它抱怨我错过了foo::afoo::b的定义,因为它认为它们是使用过的。请参阅coliru上的示例。

另一方面,gcc吞下它就好了,并愉快地打印出正确的结果。同样,这是coliru上的示例。

所以,我的问题是双重的:

  1. foo::afoo::b真的有点用吗?如果是这样,请解释为什么以及它们与foo::i的区别。

  2. 需要对lit进行哪些更改才能使其不需要foo::afoo::b的类外定义?换句话说,我希望foo::afoo::b的行为方式与foo::i相同。

  3. 更新:

    似乎巴里的答案在lit被使用的方面是正确的。为了说明,如果我添加以下功能:

    void bar(const int& x)
    {
        std::cout << "bar(" << x << ")" << std::endl;
    }
    

    以及以下第一行:

    bar(foo::i);
    

    gcc现在也抱怨。

1 个答案:

答案 0 :(得分:3)

  

foo::afoo::b真的有用吗?

我相信答案是肯定的。我们正在复制构建ablit有一个隐式的复制构造函数,它是:

constexpr lit(lit const& rhs) = default;

也就是说,我们将foo::afoo::b绑定到引用,这使得它们使用起来很常见。这不是foo::i的问题,因为int不是类类型,因此没有复制构造函数。

我不知道你能做些什么来避免这种情况,除了不复制两个文字而是复制枚举(即auto a = foo::a.value;不会使用foo::a)。