`static constexpr const char*` 和完美转发的链接器错误(未定义引用)

Linker error (undefined reference) with `static constexpr const char*` and perfect-forwarding

#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: In function `SomeStruct::SomeStruct()': ./code.cpp:(.text._ZN10SomeStructC2Ev[_ZN10SomeStructC2Ev]+0xa): undefined reference to `SomeStruct::someString' clang: error: linker command failed with exit code 1 (use -v to see invocation)


g++ -std=c++1y ./code.cpp -o code.o

/tmp/ccyrTsjS.o: In function `SomeStruct::SomeStruct()': code.cpp:(.text._ZN10SomeStructC2Ev[_ZN10SomeStructC5Ev]+0xd): undefined reference to `SomeStruct::someString' collect2: error: ld returned 1 exit status


为什么会出现此链接器错误? someString 不应该在编译时解析吗?

此外,如果将 print(someString) 替换为 cout << someString;

,则不会发生错误

因为你正在引用变量是 odr-used 并且这需要一个不合时宜的定义:

constexpr const char* SomeStruct::someString;

see it working live.

来自 C++14 标准草案部分 3.2 [basic.def.odr]:

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.20) that does not invoke any nontrivial 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 [...]

例如,下面的替代方案 print 不会 odr-use someString:

template<typename T> void print(T mX) 
{
    std::cout << mX << std::endl;  
}

在极少数情况下,您可能懒得定义一个 static 数据成员(无论它是否是 constexpr,也不管它是否利用了这种初始化形式),这不是其中之一,因为您间接获取了它的地址(因此它必须作为内存中的实际对象存在)。

你可以把它想象成一个静态数据成员,在使用时不能简单地是 "inlined",因为 what 使用是。

定义someString.