首页 > 解决方案 > 在多个文件之间拆分模板特化会导致 ODR 违规吗?

问题描述

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

我不希望基类包含有关特定类型的信息,因此我将其实现为模板并使用 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(或其他一些问题)?

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

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

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

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

标签: c++templateslanguage-lawyertemplate-specializationone-definition-rule

解决方案


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

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


推荐阅读