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

时间:2015-01-25 08:50:25

标签: c++ c++11 constexpr

我有以下代码:

#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]

2 个答案:

答案 0 :(得分:7)

类中static数据成员的声明绝不是 1 的定义。
每当变量使用odr 2 时,都需要定义。 std::get<>接受参数每个引用,并将变量绑定到引用odr-立即使用它 3

只需在外面定义names

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

Demo


1) [basic.def] / 2:

  

声明是一个定义,除非[..]声明static数据   类定义中的成员(9.2,9.4)

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

  

每个程序都应包含每个非内联的一个定义   在该程序中使用的函数或变量;没有诊断   必需的。

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

  

变量x,其名称显示为可能已评估的表达式   除非应用左值到右值的转换,否则exex使用x   (4.1)到x产生一个不调用的常量表达式(5.19)   任何非平凡的函数,如果ex是一个对象,e是一个元素   表达式e的潜在结果集合,其中任何一个   左值到右值转换(4.1)适用于e,或T::names是   丢弃值表达式(第5条)。

这里,id-expression e引用了有问题的变量。唯一包含T::names的所有潜在结果的超级表达式T::names本身就是std::get<0>(T::names),因为函数调用的潜在结果集合(即T::names)为空。但是,显然没有应用左值到右值的转换,{{1}}的值也明显不会被丢弃(因为它被传递给函数)。
因此它使用起来并且需要定义。

答案 1 :(得分:2)

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

不幸的是我正在尝试构建一个仅限标头的库。该解决方案要求将静态成员编译成一个编译单元(这是我使用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";
}