如何根据条件和有多少个参数来启用一个结构? [英] How do I enable_if a struct depending on a condition and how many arguments?

查看:318
本文介绍了如何根据条件和有多少个参数来启用一个结构?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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


  1. 如果传递一个参数,并且该参数是 int ,return bool

  2. 如果传递一个参数并且该参数是 double ,return int

  3. 如果传递了多于1个参数,则返回 double

为了达到这个目的,我尝试了以下操作:

  #include< type_traits> 

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

//多个参数;不管条件如何,返回double
模板< typename FirstArg,typename ... OtherArgs>
struct Get< typename std :: enable_if< true> :: type,FirstArg,OtherArgs ...>
{
using type = double;
};

//单一int; return bool
template< typename Arg>
struct Get< typename std :: enable_if< std :: is_same< Arg,int> :: value> :: type,Arg>
{
using type = 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:在函数'int main()':
prog.cpp:29:51:error:'type'in'struct Get< double>'不指定类型
static_assert(std :: is_same< typename Get< double> :: type,int> :: value,);
^
prog.cpp:29:60:error:模板参数1无效
static_assert(std :: is_same< typename Get< double> :: type,int& );


我会感谢一个回答,为什么这不工作的方式,我预期,不只是如何解决它。我正在努力找到有关模板元编程的良好资源,并且迄今为止编程非常随意,这是我非常想修复的。



a href =http://ideone.com/BjHaoN =nofollow>此处的示例

解决方案

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



模板中的参数



模板 < / p>

因此,原始定义:


$

b $ b

  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< true> :: 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< void,int>



第三专业:

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

同样,这匹配 Get< void,double>



专业化是模式匹配。 SFINAE enable_if 子句不能与模式匹配。所以模式匹配运行,然后评估 enable_if 子句。如果他们失败,专业化不匹配。如果它们成功, enable_if 子句会生成一个类型。在生成所有类型(模式而不是)之后,生成的类型列表匹配或不匹配。



使用这种机制的简单方法包括使您的公共版本向前到一个细节,通过 void 作为第一个类型,并在你的 enable_if 工作。



另一种方法是将你的类型绑定到一个类型列表中,例如 template< class ...> struct types {}; ,并将其作为一个参数,并将 void 作为第二个参数,并在 void 上再次执行SFINAE。



以下是一个示例:

 命名空间详细信息{
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 ...> > {};

detail :: foo 匹配 types 包。它与那些模式匹配类型的 enable_if 逻辑。如果且仅当第一种类型是 int 且有1个或多个附加类型时, enable_if 任意测试集)。


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. Let's simplify it to the following

  1. If a single argument is passed and that argument is an int, return bool
  2. If a single argument is passed and that argument is a double, return int
  3. If more than 1 argument is passed, return 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.

The ones that correspond to the original definition are after the template name.

The arguments in the template<> part of the specialization are the types introduced to match the specialization.

So original definition:

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

1 or more type arguments. 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:

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.

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. If it is an int, the first one ends up being void. So this matches Get<void, int> only.

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>.

Specialization is pattern matching. SFINAE enable_if clauses cannot be pattern matched. So the pattern match runs, then the enable_if clauses are evaluated. If they fail, the specialization does not match. If they succeed, the enable_if clause produces a type. 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.

Another way is to bundle your types up into a list of types, like template<class...>struct types{};, and pass that as one argument, and void as the second argument, and do SFINAE again on that void.

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. It does the enable_if logic with those pattern matched types. 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).

这篇关于如何根据条件和有多少个参数来启用一个结构?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆