为什么 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 不使用它们时,编译器不会抱怨。