【问题标题】:How do I enable_if a struct depending on a condition and how many arguments?如何根据条件和参数数量启用结构?
【发布时间】:2014-10-19 03:34:10
【问题描述】:

我想创建一个元函数,如果传递给它的参数超过 1 个,则返回特定类型,如果只传递一个参数,则返回基于条件的另一种类型。条件是任意的,所以它需要enable_if 或类似的东西,但在这个例子中,我只做一个类型比较。让我们将其简化为以下内容

  1. 如果传递了单个参数并且该参数是int,则返回bool
  2. 如果传递了单个参数并且该参数是double,则返回int
  3. 如果传递了多个参数,则返回double

为了实现这一点,我尝试了以下操作:

#include <type_traits>

template <typename Enable, typename...Args>                                 
struct Get;                                                     

// multiple arguments; return double regardless of the condition
template <typename FirstArg, typename... OtherArgs>                       
struct Get<typename std::enable_if<true>::type, FirstArg, OtherArgs...>
{                                                                               
    using type = double;
};

// single int; return bool
template <typename Arg>                       
struct Get<typename std::enable_if<std::is_same<Arg, int>::value>::type, Arg>
{
    using type = double;
};

// single double; return int
template <typename Arg>
struct Get<typename std::enable_if<std::is_same<Arg, double>::value>::type, Arg>
{
    using type = int;
};

int main()
{
    static_assert(std::is_same<typename Get<double>::type, int>::value, "");
    static_assert(std::is_same<typename Get<int>::type, bool>::value, "");
    static_assert(std::is_same<typename Get<bool, int>::type, double>::value, "");

    return 0;
}

输出:

prog.cpp: In function ‘int main()’:
prog.cpp:29:51: error: ‘type’ in ‘struct Get<double>’ does not name a type
  static_assert(std::is_same<typename Get<double>::type, int>::value, "");
                                                   ^
prog.cpp:29:60: error: template argument 1 is invalid
  static_assert(std::is_same<typename Get<double>::type, int>::value, "");

我会很感激能教我为什么这不能按我预期的方式工作的答案,而不仅仅是如何解决它。我一直在努力寻找关于模板元编程的好资源,并且到目前为止我的编程相当随意,这是我非常想解决的问题!

Live example here

【问题讨论】:

  • 你需要的是这个webpage
  • 这里根本不需要enable_ifcoliru.stacked-crooked.com/a/79314b2d15824360(多个参数特化实际上可以是主模板中的static_assert)。
  • @dyp 我试图创建一个简化的示例,enable_if 应该代表任意条件。我已经编辑了问题以使其更清楚。
  • 即便如此,我也不确定您是否需要enable_if 本身;您也可以将条件(作为类型函数)与默认为std::true_type 的模板参数匹配。 (有人可能会说使用enable_if 更清晰。)

标签: c++ templates


【解决方案1】:

特化的template 参数不是原始定义的模板参数。

与原始定义对应的是template名称之后。

特化的template&lt;&gt; 部分中的参数是为匹配特化而引入的类型。

原来的定义:

template <typename Enable, typename...Args>                                 
struct Get;                                                     

1 个或多个类型参数。而且,除非特化匹配,否则未定义。

第一个专业化:

template <typename FirstArg, typename... OtherArgs>                       
struct Get<typename std::enable_if<true>::type, FirstArg, OtherArgs...> {                                                                               
  using type = double;
};

好吧,std::enable_if&lt;true&gt;::type 就是 void,所以这和:

template <typename FirstArg, typename... OtherArgs>                       
struct Get<void, FirstArg, OtherArgs...> {                                                                               
  using type = double;
};

所以如果第一个类型是void,并且至少还有一个其他类型,则匹配。

第二专业:

template <typename Arg>                       
struct Get<typename std::enable_if<std::is_same<Arg, int>::value>::type, Arg> {
  using type = double;
};

所以如果有两种类型,它会尝试匹配。第二个是模式匹配。

如果不是int,则第一个 SFINAE 失败。如果是int,则第一个最终是void。所以这只匹配Get&lt;void, int&gt;

第三专业:

template <typename Arg>
struct Get<typename std::enable_if<std::is_same<Arg, double>::value>::type, Arg> {
  using type = int;
};

同样,这匹配Get&lt;void, double&gt;

专业化是模式匹配。 SFINAE enable_if 子句不能进行模式匹配。因此模式匹配运行,然后评估 enable_if 子句。如果失败,则专业化不匹配。如果成功,enable_if 子句会生成一个类型。在生成所有类型(模式和非模式)之后,类型的结果列表要么匹配,要么不匹配。

使用此机制的简单方法包括将您的公开版本转发到详细信息版本,将void 作为第一种类型传递,并在那里完成您的enable_if 工作。

另一种方法是将您的类型捆绑到一个类型列表中,例如 template&lt;class...&gt;struct types{};,并将其作为一个参数传递,void 作为第二个参数传递,然后在该 void 上再次执行 SFINAE。

这是一个例子:

namespace details {
  template<class...>struct types{};
  template<class Types, class=void>
  struct foo;
  template<class T0, class... Ts>
  struct foo<types<T0, Ts...>,typename std::enable_if<
    std::is_same<T0, int>::value && (sizeof...(Ts)>=1)
  >> {
    using type=double;
  };
}
template<class T0, class... Ts>
struct foo:details::foo< details::types<T0, Ts...> >{};

details::foo 模式的特化匹配 types 包。它使用那些模式匹配类型执行enable_if 逻辑。 enable_if 当且仅当第一个类型是 int 并且有 1 个或多个附加类型(对于任意一组测试)时才会通过。

【讨论】:

    猜你喜欢
    • 2022-07-06
    • 2022-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-01
    • 1970-01-01
    • 2021-07-02
    • 2017-07-09
    相关资源
    最近更新 更多