为什么 constexpr 解决重复定义?
Why is constexpr solving duplicated definition?
我有一个 header 文件,其中字符串被定义为静态全局。
namespace space {
#define NAME(P) static std::string const s_##P = #P
NAME(foo); NAME(bar); //... other values
#undef NAME
}
在另一个 header 中,定义了一个枚举,模板特化提供了 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
当 header 包含在多个翻译单元中时,上述代码不会 link 因为专业化定义不匹配 - 由于每个翻译单元的全局重新定义(我猜) .
在匿名命名空间中包装映射函数或添加 static
关键字解决了 linking 问题,但随后编译器抱怨函数是 defined but not used [-Wunused-function]
.
template<letter> static std::string const & mapping();
但是,将专业化定义为 constexpr
,不再有任何 link 或警告问题。
template<letter> std::string const & mapping();
#define MAPPING(P1,P2) template<> constexpr std::string const & mapping<letter::P1>() { return space::s_##P2; }
我明白为什么非 static
版本在 link 时失败以及为什么 static
版本工作并触发警告。但我不明白为什么 constexpr
说明符可以解决这两个问题。
能否请您给出解释,甚至更好,标准中的合理性?
使用 constexpr 说明符声明的函数是内联函数。
来自 C++ 20 标准(9.2.5 constexpr 和 consteval 说明符)
1 The constexpr specifier shall be applied only to the definition of a
variable or variable template or the declaration of a function or
function template. The consteval specifier shall be applied only to
the declaration of a function or function template. A function or
static data member declared with the constexpr or consteval specifier
is implicitly an inline function or variable (
函数模板特化是函数,因此与非模板特化的函数一样遵守单一定义规则。
您在声明函数时看到的链接器错误既不是 static
也不是 constexpr
是由于同一函数模板特化的多个定义,每个都有外部链接。
当您添加 static
时,您将链接设置为内部链接。这使得每个翻译单元都可以安全地包含自己的定义副本。然而,在任何未调用这些函数的 TU 中,编译器知道(由于内部链接)它们也不能从任何其他 TU 调用,从而使它们未被使用。
使用constexpr
,函数根据标准隐式地内联,但它们的链接不受影响。由于它们是内联的,你可以有多个定义,但由于它们有外部链接,当一个 TU 不使用它们时,编译器不会抱怨。
我有一个 header 文件,其中字符串被定义为静态全局。
namespace space {
#define NAME(P) static std::string const s_##P = #P
NAME(foo); NAME(bar); //... other values
#undef NAME
}
在另一个 header 中,定义了一个枚举,模板特化提供了 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
当 header 包含在多个翻译单元中时,上述代码不会 link 因为专业化定义不匹配 - 由于每个翻译单元的全局重新定义(我猜) .
在匿名命名空间中包装映射函数或添加 static
关键字解决了 linking 问题,但随后编译器抱怨函数是 defined but not used [-Wunused-function]
.
template<letter> static std::string const & mapping();
但是,将专业化定义为 constexpr
,不再有任何 link 或警告问题。
template<letter> std::string const & mapping();
#define MAPPING(P1,P2) template<> constexpr std::string const & mapping<letter::P1>() { return space::s_##P2; }
我明白为什么非 static
版本在 link 时失败以及为什么 static
版本工作并触发警告。但我不明白为什么 constexpr
说明符可以解决这两个问题。
能否请您给出解释,甚至更好,标准中的合理性?
使用 constexpr 说明符声明的函数是内联函数。
来自 C++ 20 标准(9.2.5 constexpr 和 consteval 说明符)
1 The constexpr specifier shall be applied only to the definition of a variable or variable template or the declaration of a function or function template. The consteval specifier shall be applied only to the declaration of a function or function template. A function or static data member declared with the constexpr or consteval specifier is implicitly an inline function or variable (
函数模板特化是函数,因此与非模板特化的函数一样遵守单一定义规则。
您在声明函数时看到的链接器错误既不是 static
也不是 constexpr
是由于同一函数模板特化的多个定义,每个都有外部链接。
当您添加 static
时,您将链接设置为内部链接。这使得每个翻译单元都可以安全地包含自己的定义副本。然而,在任何未调用这些函数的 TU 中,编译器知道(由于内部链接)它们也不能从任何其他 TU 调用,从而使它们未被使用。
使用constexpr
,函数根据标准隐式地内联,但它们的链接不受影响。由于它们是内联的,你可以有多个定义,但由于它们有外部链接,当一个 TU 不使用它们时,编译器不会抱怨。