【发布时间】: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<typename T> struct EnumTrait;- 声明但没有定义。 -
我在这里看不到任何特殊的 ODR 问题(尽管我认为
-1,即使在转换为无符号之后,在技术上可能超出sizeof的值范围,因此格式错误,不需要诊断 - 但我不会担心)。我可以推荐language-lawyer 标签吗? -
@MaximEgorushkin 感谢您的建议。不过,我想
static_assert的错误信息会更清楚一些。 @MaxLanghof 我想我可以将其更改为0,谢谢。我将添加语言律师标签。
标签: c++ templates language-lawyer template-specialization one-definition-rule