我有一个头文件,其中字符串被定义为静态全局。
namespace space {
#define NAME(P) static std::string const s_##P = #P
NAME(foo); NAME(bar); //... other values
#undef NAME
}
在另一个标头中,定义了一个枚举,模板专用化提供了枚举和space
中的字符串之间的映射。
enum class letter { alpha, beta };
template<letter> std::string const & mapping();
#define MAPPING(P1,P2) template<> std::string const & mapping<letter::P1>() { return space::s_##P2; }
MAPPING(alpha,foo)
MAPPING(beta,bar)
#undef MAPPING
当标头包含在多个翻译单元中时,上面的代码不会链接,因为专业化定义不匹配——这是由于每个翻译单元的全局重新定义(我想(。
将映射函数封装在匿名命名空间中或添加static
关键字可以解决链接问题,但编译器会抱怨这些函数是defined but not used [-Wunused-function]
。
template<letter> static std::string const & mapping();
但是,将专业化定义为constexpr
,就不再存在任何链接或警告问题。
template<letter> std::string const & mapping();
#define MAPPING(P1,P2) template<> constexpr std::string const & mapping<letter::P1>() { return space::s_##P2; }
我理解为什么非static
版本在链接时失败,以及为什么static
版本工作并触发警告。但我不明白constexpr
说明符为什么能解决这两个问题。
你能给出一个解释,甚至更好的是,在标准中给出一个理性的解释吗?
函数模板专业化是函数,因此与非模板专业化的函数一样,受一个定义规则的约束。
当函数未声明为static
或constexpr
时,您看到的链接器错误是由于同一函数模板专门化的多个定义造成的,每个定义都有外部链接。
添加static
时,已将链接设置为内部链接。这使得每个翻译单元都可以安全地包含自己的定义副本。然而,在任何没有调用这些函数的TU中,编译器都知道(由于内部链接(它们也不能从任何其他TU调用,因此无法使用它们。
使用constexpr
,函数根据标准隐式内联,但它们的链接不受影响。由于它们是内联的,您可以有多个定义,但由于它们有外部链接,当一个TU不使用它们时,编译器不会抱怨。
函数是内联函数。
来自C++20标准(9.2.5 constexpr和consteval说明符(
1 constexpr说明符应仅应用于变量或变量模板或函数或函数模板。consteval说明符应仅应用于函数或函数模板的声明A函数或使用constexpr或consteval说明符声明的静态数据成员是隐式的内联函数或变量(