我有一个通用模板类,我不想(无法)更改它
template <typename TData>
class Printer {
public:
bool Write(std::ostream& ostr, const TData& data) const {
ostr << data;
return true;
}
};
我想为
Printer
实现一个专门的类,仅适用于枚举
template <typename TThatIsEnum>
class Printer{
public:
bool Write(std::ostream& ostr, const TThatIsEnum& data) const {
static Printer<int> int_printer;
return int_printer.Write(ostr, static_cast<int>(data));
}
};
如果不向通用模板添加额外的模板参数,这可能吗?
就目前情况而言,在 C++17 中,您没有任何东西可以专门化主模板on。不是可以检测任意类型特征的方式。
标准程序是向主参数添加另一个虚拟参数。
template <typename TData, typename = void>
class Printer {
public:
bool Write(std::ostream& ostr, const TData& data) const {
ostr << data;
return true;
}
};
它是“内部的”,并不意味着由客户端代码指定。相反,客户端仅指定第一个,模板机制计算出第二个。专业化看起来像这样
template <typename Enum>
class Printer<Enum, std::enable_if_t<std::is_enum_v<Enum>>> {
Printer<std::underlying_type_t<Enum>> _delegate;
public:
bool Write(std::ostream& ostr, const Enum& data) const {
return _delegate.Write(ostr, static_cast<std::underlying_type_t<Enum>>(data));
}
};
当从客户端的参数中选择专业化时,编译器将实例化特征。对于非枚举
std::enable_if_t<...>
的格式不正确,因此我们回到主要的。它的第二个参数有一个默认参数,因此它可以工作。
对于枚举
std::enable_if_t<...>
将是 void
,因此我们有一个有效的专业化(与主要的可能参数匹配)。我们可以为主要参数提供任意参数(例如Printer<char, int>
),但并不是每对参数都对我们的专业化有效(int
永远不会匹配void
)。另一方面,我们专业化的所有有效参数也将适合主要参数(这就是为什么默认参数是 void
,因此它匹配),因此专业化确实被认为更专业化。
我还冒昧地改进了你的实现,询问枚举的底层类型是什么(而不是假设
int
)。对于库来说,使用成员委托比静态更友好。对象大小相同,但惊喜较少。
最后,您应该知道 C++20 通过类型约束和概念大大简化了这一过程。在 C++20 中我们可以这样写
template <typename TData>
class Printer {
// As you did
};
template <typename Enum>
requires std::is_enum_v<Enum>
class Printer {
// As I did
};
不需要虚拟参数,因为约束成为一等公民。