【问题标题】:Generic `Is_enabled` SFINAE structure通用 `Is_enabled` SFINAE 结构
【发布时间】:2019-06-08 16:28:09
【问题描述】:

我的目标是实现一个结构模板,该模板可用于检测模板替换是否格式正确或将失败。一个使用示例是根据模板参数是否具有可比性,提供两个版本的模板函数。

如果明确地为每个场景提供结构,则可以很容易地解决这个问题,例如模板类型是否存在相等运算符,如图here。但我未能实现一个可以接受(几乎)任意构造作为模板参数的结构。

到目前为止,我已经达到的“最佳”方法使用模板模板参数。它可以编译,但它不适合应该正确形成参数替换的情况。

#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 的特化永远不会完成。

我是否有点接近正确的解决方案,还是会更复杂?

【问题讨论】:

  • using Enable = void;,即void不是T
  • 确实解决了我的问题。有人可以向我解释为什么Enable = T 的解决方案不能按我的预期工作吗?

标签: c++ templates sfinae


【解决方案1】:

Is_enabled 的第三个模板参数默认为void。这就是编译器将在Is_enabled&lt;T, Equals&gt; 实例化中使用的内容。也就是说,只有当Enable&lt;X&lt;T&gt;&gt; 的计算结果为void 时,才能使用Is_enabled&lt;T, X, Enable&lt;X&lt;T&gt;&gt;&gt; : std::true_type {};。通过将模板参数X&lt;T&gt; 显式传递给类模板Enable,声明为:

template <typename T = void, typename...>
using Enable = T;

您实际上为X&lt;T&gt; 本身创建了一个别名,并且根本不使用void 类型(默认类型,调度工作所需的)。在您的情况下,X&lt;T&gt;decltype 说明符的结果。对于foo&lt;A&gt;(),它确实会导致实例化失败。但是,对于foo&lt;int&gt;(),您会得到整数比较的结果类型,即bool。也就是说,虽然没有替换失败,但编译器不能使用类模板特化,因为它是为void 特化的,而不是bool

为了修复代码,您应该重写Enable 以始终使用void

template <typename...>
using Enable = void;

这也称为std::void_t

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-16
    • 1970-01-01
    • 1970-01-01
    • 2018-06-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多