#include <iostream>
using namespace std;
template<typename T> void print(T&& mX)
{
std::cout << std::forward<T>(mX) << std::endl;
}
struct SomeStruct
{
static constexpr const char* someString{"hello!"};
SomeStruct()
{
print(someString);
}
};
int main()
{
SomeStruct s{};
return 0;
}
clang++ -std=c++1y ./code.cpp -o code.o
/tmp/code-a049fe.o:在函数`SomeStruct :: SomeStruct()'中: ./code.cpp:(.text._ZN10SomeStructC2Ev[_ZN10SomeStructC2Ev]+0xa): 未命名的引用`SomeStruct :: someString'clang:错误:链接器 命令失败,退出代码为1(使用-v查看调用)
g++ -std=c++1y ./code.cpp -o code.o
/tmp/ccyrTsjS.o:在函数`SomeStruct :: SomeStruct()'中: code.cpp :( text._ZN10SomeStructC2Ev [_ZN10SomeStructC5Ev] + 0xd中): 未定义的引用`SomeStruct :: someString'colle2:error:ld 返回1退出状态
为什么会发生此链接器错误?在编译时不应该someString
可以解析吗?
此外,如果print(someString)
替换为cout << someString;
答案 0 :(得分:5)
因为您正在参考该变量是使用了odr,这需要一个不符合要求的定义:
constexpr const char* SomeStruct::someString;
从草案C ++ 14标准部分3.2
[basic.def.odr] :
变量x的名称显示为潜在评估的表达式ex,除非应用,否则由ex使用 左值到右值的转换(4.1)到x产生一个不调用任何非平凡的常量表达式(5.20) 函数,如果x是一个对象,ex是表达式e的潜在结果集的一个元素, 将左值到右值转换(4.1)应用于e,或者e是丢弃值表达式[...]
例如,以下备选print
不会使用someString
:
template<typename T> void print(T mX)
{
std::cout << mX << std::endl;
}
答案 1 :(得分:2)
在一系列情况下,您可能无法定义static
数据成员(无论是否为constexpr
,以及是否利用此初始化表单),这不是其中之一,因为你间接获取它的地址(因此它必须作为内存中的实际对象存在)。
您可以将其视为一个静态数据成员,由于使用的是什么而无法简单地在其使用时“内联”。
定义someString
。