【问题标题】:Can splitting template specializations between multiple files lead to ODR violations?在多个文件之间拆分模板特化会导致 ODR 违规吗?
【发布时间】:2019-09-10 13:24:04
【问题描述】:

我编写了一个解析器类,其中包含其他特定类型解析器的基本功能。例如,它包含一个从字符串创建枚举值的函数。

我不希望基类包含有关特定类型的信息,因此我将其实现为模板并使用 trait 来避免包含特定类型的标头:

// ParserBase.h
#include "EnumTrait.h"
template<typename EnumT>
EnumT parseEnum(std::string str)
{
  return traits::EnumTrait<EnumT>::fromString(str);
}

EnumTrait 模板的定义如下:

// EnumTrait.h
namespace traits
{
template<typename T>
struct EnumTrait
{
  static_assert(sizeof(T) == -1, "Specialization not found");
};
} // namespace traits

现在,在定义我的枚举的每个标头中,也有此模板的特化。例如:

// Enum_A.h
#include "EnumTrait.h"
namespace A
{
enum class Enum_A
{
  A
};
Enum_A fromString(std::string) {return Enum_A::A;}
} // namespace A

namespace traits
{
template<>
struct EnumTrait<A::Enum_A>
{
  static std::string fromString(std::string str){ return A::fromString(str); }
};
// namespace traits

其他枚举的标题看起来相似。

基函数的用法:

// Enum_AParser.cpp
#include "ParserBase.h"
#include "Enum_A.h"
// ...
Enum_A foo = parseEnum<Enum_A>(bar);
// ...

我担心的是:这(是否)会导致 ODR 违规(或其他一些问题)?

不可能将 trait 与 Enum_A 一起使用并且没有可用的特化,因为它们是在同一个标​​头中定义的。

但是在使用模板的每个 TU 中不提供每个模板特化是否可以(例如 Enum_A 在 Enum_BParser 中不可用)?

在考虑这个问题时我注意到的一件事是,创建我们自己的标准库中可用的模板专业化是合法的,所以也许它毕竟是可以的?

我正在使用 C++17,如果这有什么改变的话。

【问题讨论】:

  • 通用版本可以更简单:template&lt;typename T&gt; struct EnumTrait; - 声明但没有定义。
  • 我在这里看不到任何特殊的 ODR 问题(尽管我认为 -1,即使在转换为无符号之后,在技术上可能超出 sizeof 的值范围,因此格式错误,不需要诊断 - 但我不会担心)。我可以推荐language-lawyer 标签吗?
  • @MaximEgorushkin 感谢您的建议。不过,我想static_assert 的错误信息会更清楚一些。 @MaxLanghof 我想我可以将其更改为 0,谢谢。我将添加语言律师标签。

标签: c++ templates language-lawyer template-specialization one-definition-rule


【解决方案1】:

我觉得不错。并不要求每个专业化在每个翻译单元中都可见。只需要在每次使用模板之前声明特化,否则会触发隐式实例化。

[temp.expl.spec]/7 如果模板、成员模板或类模板的成员被显式特化,则该特化应在第一次使用该特化之前声明,这将导致在每个翻译单元中发生隐式实例化发生此类使用的地点;不需要诊断...

【讨论】:

    猜你喜欢
    • 2011-11-29
    • 1970-01-01
    • 2016-09-13
    • 1970-01-01
    • 1970-01-01
    • 2016-04-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多