c++ - 通用 `Is_enabled` SFINAE 结构
问题描述
我的目标是实现一个结构模板,该模板可用于检测模板替换是否格式正确或将失败。一个使用示例是根据模板参数是否具有可比性,提供两个版本的模板函数。
如果明确地为每个场景提供结构,则可以很容易地解决这个问题,例如是否存在模板类型的相等运算符,如此处所示。但是我未能实现一个可以接受(几乎)任意构造作为模板参数的结构。
到目前为止,我已经达到的“最佳”方法使用模板模板参数。它可以编译,但不适合应该正确形成参数替换的情况。
#include <iostream>
#include <type_traits>
template <typename T = void, typename...>
using Enable = T;
template <bool Cond, typename T = void>
using Enable_if = typename std::enable_if<Cond, T>::type;
template <typename T, template<typename> class X, typename = void>
struct Is_enabled : std::false_type {};
template <typename T, template<typename> class X>
struct Is_enabled<T, X, Enable<X<T>>> : std::true_type {};
/// An example of construct
template <typename T>
using Equals = decltype(std::declval<T>() == std::declval<T>());
template <typename T>
using Enabled_eq = Enable_if<Is_enabled<T, Equals>::value>;
template <typename T>
using Disabled_eq = Enable_if<!Is_enabled<T, Equals>::value>;
template <typename T>
Enabled_eq<T> foo()
{
std::cerr << "enabled!" << std::endl;
}
template <typename T>
Disabled_eq<T> foo()
{
std::cerr << "disabled!" << std::endl;
}
struct A {};
int main(int /*argc*/, const char* /*argv*/[])
{
foo<int>(); /// should print "enabled!"
foo<A>(); /// should print "disabled!"
return 0;
}
万一int
,它显然应该打印"enabled!"
,万一A
它应该打印"disabled!"
。但它总是打印"disabled!"
,所以Is_enabled
从未完成专业化。
我是否有点接近正确的解决方案,还是会更复杂?
解决方案
第三个模板参数Is_enabled
默认为void
. 这是编译器将在Is_enabled<T, Equals>
实例化中使用的内容。也就是说,Is_enabled<T, X, Enable<X<T>>> : std::true_type {};
只有在Enable<X<T>>
评估为时才能使用void
。通过将模板参数显式传递给声明为X<T>
的类模板Enable
:
template <typename T = void, typename...>
using Enable = T;
X<T>
您实际上为自己创建了一个别名,并且void
根本不使用类型(调度工作所需的默认类型)。在您的情况下,X<T>
是说明decltype
符的结果。因为foo<A>()
它确实会导致实例化失败。但是,对于foo<int>()
,您会得到整数比较的结果类型,即bool
. 也就是说,虽然没有替换失败,但编译器不能使用类模板特化,因为它是特化的void
,而不是特化的bool
。
为了修复代码,您应该重写Enable
以始终使用以下结果void
:
template <typename...>
using Enable = void;
这也称为std::void_t
.
推荐阅读
- jquery - 如何在 ajax 成功中将 id 传递给我的打印页面
- ios - 如何在不使用导航控制器提供的默认导航栏的情况下快速制作自定义导航栏
- php - 从多维数组中提取同名数组
- javascript - 单击另一个select2下拉菜单时如何保持select2下拉事件打开
- javafx - '@FXML' 不适用于类型
- hyperledger-fabric - 超级账本结构休息 API 到智能合约
- c - 如何在 Windows 的 gcc mingw 编译器中为 c 程序安装 libxml2?
- php - 如何在codeigniter控制器的回调函数中调用javascript函数?
- android - 关于 Google Play Console 每日 APK 和捆绑包限制
- java - 我在制作可查找用户 lat/lng 和设备 IMEI 的应用时遇到问题