是否可以混合 SFINAE 和模板专业化?
Posted
技术标签:
【中文标题】是否可以混合 SFINAE 和模板专业化?【英文标题】:Is it possible to mix SFINAE and template specialisation? 【发布时间】:2020-10-13 01:55:58 【问题描述】:这是我大致想要实现的目标:
// 声明 模板 结构 ArgsEstimate; // 字符串的特化,SFINAE 将是矫枉过正 模板 结构 ArgsEstimate<:string args...> 静态常量 std::size_t 大小 = 64 + ArgsEstimate::size; ; // 算术类型的特化 模板::value>::type* = nullptr, 类型名... Args> 结构 ArgsEstimate 静态常量 std::size_t 大小 = sizeof(AirthmeticT) + ArgsEstimate::size; ; // 指针类型的特化 模板::value>::type* = nullptr, 类型名... Args> 结构 ArgsEstimate静态常量 std::size_t 大小 = 32 + ArgsEstimate::size; ;
问题是这段代码在我已经完成enable_if
的点上给出了编译错误“模板参数在部分特化中不可推导”。结构中的static_assert
也不起作用,因为会重新定义。
我知道,我可以单独使用 SFINAE 和函数重载来做到这一点。但是,对于像 std::string
这样的情况,使用 SFINAE 是大材小用。
所以我想知道是否有混合模板专业化和 SFINAE 的干净方式。
【问题讨论】:
【参考方案1】:直接回答您的问题
你可以,但你真的不能。您的案例因可变参数模板参数而变得复杂。
// specialisation for arithmetic types
template<class AirthmeticT, class... Args>
struct ArgsEstimate<
AirthmeticT,
std::enable_if_t<std::is_arithmetic_v<AirthmeticT>>,
Args...>
static const std::size_t size = sizeof(AirthmeticT) + ArgsEstimate<Args...>::size;
;
这行得通……有点。您只需要确保第二个参数始终为 void:
ArgsEstimate<int, void, /* ... */> ok; // will use the integer specialization
ArgsEstimate<int, int, int> wrong; // oups, will use the base template.
这是不切实际的。
C++20 概念
概念优雅地解决了这个问题:
// specialisation for arithmetic types
template<class T, class... Args>
requires std::is_arithmetic_v<T>
struct ArgsEstimate<T, Args...>
static const std::size_t size = sizeof(T) + ArgsEstimate<Args...>::size;
;
前置概念解决方案
您需要做的是将您的班级分成两个班级。只为 1 个参数定义大小的一种。在这里您可以使用 SFINAE。另一个总结它们:
template <class T, class Enable = void>
struct ArgEstimate ;
// specialisation for string, SFINAE would be overkill
template<>
struct ArgEstimate<std::string&>
static const std::size_t size = 64;
;
// specialisation for arithmetic types
template<class T>
struct ArgEstimate<T, std::enable_if_t<std::is_arithmetic_v<T>>>
static const std::size_t size = sizeof(T);
;
// specialisation for pointer types
template <class T>
struct ArgEstimate<T*>
static const std::size_t size = 32;
;
// the declaration
template<class... Args> struct ArgsEstimate;
template<class T>
struct ArgsEstimate<T>
static const std::size_t size = ArgEstimate<T>::size;
;
template<class Head, class... Tail>
struct ArgsEstimate<Head, Tail...>
static const std::size_t size = ArgEstimate<Head>::size + ArgsEstimate<Tail...>::size;
;
如果你有 C++17,你可以使用折叠表达式来简化总和:
template<class... Args>
struct ArgsEstimate
static const std::size_t size = (... + ArgEstimate<Args>::size);
;
还只是想指出,指针不需要 SFINAE:
// specialisation for pointer types
template <class T, class... Args>
struct ArgsEstimate<T*, Args...>
static const std::size_t size = 32 + ArgsEstimate<Args...>::size;
;
【讨论】:
以上是关于是否可以混合 SFINAE 和模板专业化?的主要内容,如果未能解决你的问题,请参考以下文章