我有以下代码:
#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);
}
由于names
是constexpr
我希望此功能正常。但我收到链接器错误:
编译器:
> 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)
答案 0 :(得分:7)
类中static
数据成员的声明绝不是 1 的定义。
每当变量使用odr 2 时,都需要定义。
std::get<>
接受参数每个引用,并将变量绑定到引用odr-立即使用它 3 。
只需在外面定义names
:
constexpr T::Names T::names; // Edit: This goes *outside* the class "as is"!
1) [basic.def] / 2:
声明是一个定义,除非[..]声明
static
数据 类定义中的成员(9.2,9.4)
2) [basic.def.odr] / 4:
每个程序都应包含每个非内联的一个定义 在该程序中使用的函数或变量;没有诊断 必需的。
3)根据[basic.def.odr] / 3:
变量
x
,其名称显示为可能已评估的表达式 除非应用左值到右值的转换,否则ex
会ex
使用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";
}