简体   繁体   English

如何根据条件和多少个参数启用结构?

[英]How do I enable_if a struct depending on a condition and how many arguments?

I want to create a meta function that returns a certain type if more than 1 argument is passed to it, and another type based on a condition if only a single argument is passed to it. 我想创建一个元函数,如果将一个以上的参数传递给它,则返回某种类型;如果仅将一个参数传递给它,则根据条件返回另一种类型。 The condition is arbitrary so it will require an enable_if or something of that kind, but for this example I'll just make it a type comparison. 该条件是任意的,因此将需要一个enable_if或类似类型的东西,但在本示例中,我将其enable_if类型比较。 Let's simplify it to the following 让我们简化为以下内容

  1. If a single argument is passed and that argument is an int , return bool 如果传递了单个参数并且该参数是int ,则返回bool
  2. If a single argument is passed and that argument is a double , return int 如果传递了单个参数而该参数是double ,则返回int
  3. If more than 1 argument is passed, return double 如果传递了多个参数,则返回double

To achieve this I have tried to do the following: 为此,我尝试执行以下操作:

#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;
}

Output: 输出:

 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, ""); 

I would be grateful for an answer that teaches me why this doesn't work the way I expected, not just how to fix it. 我将不胜感激,因为它能教给我一个答案, 为什么它不能按我预期的方式工作,而不仅仅是如何解决它。 I'm struggling to find good resources on template meta-programming and have so far been programming rather haphazardly, which is something I'd very much like to fix! 我正在努力寻找有关模板元编程的良好资源,到目前为止,我一直很随意地进行编程,这是我非常想解决的问题!

Live example here 这里的例子

The template arguments to a specialization are not the template arguments to the original definition. template参数的特化不是模板参数的原始定义。

The ones that correspond to the original definition are after the template name. 对应于原始定义的名称位于template名称之后。

The arguments in the template<> part of the specialization are the types introduced to match the specialization. 专业化的template<>部分中的参数是为匹配专业化而引入的类型。

So original definition: 所以原来的定义:

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

1 or more type arguments. 1个或多个类型参数。 And, unless a specialization matches, not defined. 并且,除非专业匹配,否则未定义。

First specialization: 第一专业化:

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

well, std::enable_if<true>::type is just void , so this is the same as: 好吧, std::enable_if<true>::type只是void ,因此与以下内容相同:

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

So this matches if the first type is void , and there is at least one other type. 因此,如果第一个类型为void ,并且至少还有一个其他类型,则此匹配。

Second specialization: 第二专业化:

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

So this tries to match if there are two types. 因此,如果有两种类型,它将尝试匹配。 The second one is pattern matched. 第二个是模式匹配。

If it isn't int , the first one SFINAE fails. 如果不是int ,则第一个SFINAE失败。 If it is an int , the first one ends up being void . 如果它是一个int ,第一个最终被void So this matches Get<void, int> only. 因此,这仅匹配Get<void, int>

Third specialization: 第三专业化:

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

similarly, this matches Get<void, double> . 同样,它匹配Get<void, double>

Specialization is pattern matching. 专业是模式匹配。 SFINAE enable_if clauses cannot be pattern matched. SFINAE enable_if子句无法进行模式匹配。 So the pattern match runs, then the enable_if clauses are evaluated. 因此,运行模式匹配,然后评估enable_if子句。 If they fail, the specialization does not match. 如果它们失败,则专业化将不匹配。 If they succeed, the enable_if clause produces a type. 如果它们成功,则enable_if子句产生一个类型。 After all types are generated (pattern and not), the resulting list of types either matches or does not. 生成所有类型(不是模式)之后,类型的结果列表要么匹配,要么不匹配。

The easy ways to use this machinery include having your public version forward to a details one, passing void as the first type, and doing your enable_if work there. 使用此机制的简单方法包括将您的公共版本转发到详细信息,将void作为第一种类型传递,并在那里进行enable_if工作。

Another way is to bundle your types up into a list of types, like template<class...>struct types{}; 另一种方法是将类型捆绑为类型列表,例如template<class...>struct types{}; , and pass that as one argument, and void as the second argument, and do SFINAE again on that void . ,并将其作为一个参数传递,并将void作为第二个参数传递,然后对该void再次执行SFINAE。

Here is an example: 这是一个例子:

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...> >{};

The specialization of details::foo pattern matches the types bundle. details::foo模式的特殊化与types包匹配。 It does the enable_if logic with those pattern matched types. 它使用那些模式匹配的类型执行enable_if逻辑。 The enable_if passes if and only if the first type is an int and there are 1 or more additional types (for an arbitrary set of tests). 当且仅当第一种类型为int且存在一种或多种其他类型(用于任意测试集)时, enable_if通过。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何 enable_if 具有可变参数模板参数的类? - How do I enable_if a class with variadic template arguments? 如何根据模板类型使用 std::enable_if 启用或禁用构造函数? - How do I use std::enable_if to enable or disable constructors depending on template types? 仅在给定n个以上参数的情况下,才能启用可变参数模板构造函数? - How do I enable_if a variadic template constructor only if more than n arguments are given? 如何在此特定函数声明中正确使用enable_if? - How do I correctly use enable_if in this specific function declaration? 使用 enable_if 时如何解决此错误:“struct std::enable_if 中没有名为 'type' 的类型<false, void> ’”</false,> - How to slove this error when using enable_if: “no type named ‘type’ in ‘struct std::enable_if<false, void>’” 如何正确使用enable_if? - How to use enable_if correctly? 如何将std :: enable_if与自推理返回类型一起使用? - How do I use std::enable_if with a self-deducing return type? sfinae:enable_if条件 - sfinae: enable_if condition 如何使用enable_if &lt;&gt; :: type返回类型原型化函数? - How do you prototype a function with an enable_if<>::type return type? 如何在转换运算符中使用std :: enable_if? - how can I use std::enable_if in a conversion operator?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM