2

我有一个头文件,其中字符串被定义为静态全局。

namespace space {
#define NAME(P) static std::string const s_##P = #P
        NAME(foo); NAME(bar); //... other values
#undef  NAME
}

在另一个头文件中,定义了一个枚举,并且模板特化提供了枚举和 .in 中的字符串之间的映射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符解决了这两个问题。

您能否在标准中给出一个解释,甚至更好,一个理性的?

4

2 回答 2

4

函数模板特化是函数,因此以与不是模板特化的函数相同的方式服从单一定义规则。

您在声明函数时看到的链接器错误既不是static也不constexpr是由于同一函数模板特化的多个定义,每个特化都有外部链接。

添加时static,您将链接设为内部。这使得每个翻译单元都可以安全地包含自己的定义副本。但是,在没有调用这些函数的任何 TU 中,编译器知道(由于内部链接)它们也不能从任何其他 TU 调用,从而使它们未被使用。

使用constexpr,函数根据标准隐式变为内联,但它们的链接不受影响。由于它们是内联的,因此您可以有多个定义,但由于它们具有外部链接,因此当一个 TU 不使用它们时编译器不会抱怨。

于 2020-02-27T22:11:30.737 回答
1

用 constexpr 说明符声明的函数是内联函数。

来自 C++ 20 标准(9.2.5 constexpr 和 consteval 说明符)

1 constexpr 说明符应仅应用于变量或变量模板的定义或函数或函数模板的声明。consteval 说明符仅应用于函数或函数模板的声明。使用 constexpr 或 consteval 说明符声明的函数或静态数据成员隐式是内联函数或变量 (

于 2020-02-27T22:07:08.270 回答