静态元组 class 成员的 constexpr 有链接器错误

constexpr of static tuple class member has linker error

我有以下代码:

#include <iostream>
#include <tuple>

class T
{
    public:
        using Names = std::tuple<char const*, char const*>;
        static constexpr Names names {"First", "Second"};
};

int main()
{
    std::cout << std::get<0>(T::names);
}

因为 namesconstexpr 我希望它能工作。但是我收到链接器错误:

编译器:

> g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin14.0.0
Thread model: posix

错误:

> g++ -std=c++1y pl.cpp
Undefined symbols for architecture x86_64:
  "T::names", referenced from:
      _main in pl-377031.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

[live demo]

class 中 static 数据成员的声明永远不是定义1.
当一个变量被 odr-used2 时,定义是必要的。 std::get<> 每个引用接受参数 ,并将变量绑定到引用 odr-立即使用它3

在外面简单定义names

constexpr T::Names T::names; // Edit: This goes *outside* the class "as is"!

Demo.


1) [basic.def]/2:

A declaration is a definition unless [..] it declares a static data member in a class definition (9.2, 9.4)

2) [basic.def.odr]/4:

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required.

3) 根据 [basic.def.odr]/3:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.19) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression (Clause 5).

这里的 id-expression T::names 指的是有问题的变量。唯一包含 T::names 的所有潜在结果的超表达式 eT::names 本身,因为函数调用的潜在结果集,即 std::get<0>(T::names) 是空的。但是,显然没有应用左值到右值的转换,并且 T::names 的值也显然没有被丢弃(因为它被传递给函数)。
因此它是 odr-used 并且需要一个定义。

@Columbo 发布了正确的解决方案。

不幸的是,我正在尝试构建一个仅 header 的库。该解决方案要求将静态成员编译为一个编译单元(这是我希望避免使用的 constexpr)。

所以我需要在作品中加入另一种扭曲以使其发挥作用。这只是为了分享我的解决方案:

#include <iostream>

class T
{
    public:
        using Names = std::tuple<char const*, char const*>;
        template<std::size_t index>
        static char const* getName()
        {
            static constexpr Names names {"First", "Second"};
            return std::get<index>(names);
        }
};

int main()
{
    std::cout << T::getName<0>() << "\n";
}