静态元组 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);
}
因为 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)
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
的所有潜在结果的超表达式 e
是 T::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";
}
我有以下代码:
#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)
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 expressionex
is odr-used byex
unless applying the lvalue-to-rvalue conversion (4.1) tox
yields a constant expression (5.19) that does not invoke any non-trivial functions and, ifx
is an object,ex
is an element of the set of potential results of an expressione
, where either the lvalue-to-rvalue conversion (4.1) is applied toe
, ore
is a discarded-value expression (Clause 5).
这里的 id-expression T::names
指的是有问题的变量。唯一包含 T::names
的所有潜在结果的超表达式 e
是 T::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";
}