如何根据条件和有多少个参数来启用一个结构? [英] How do I enable_if a struct depending on a condition and how many arguments?
问题描述
我想创建一个元函数,如果超过1个参数传递给它返回一个特定类型,另一个类型基于一个条件,如果只传递一个参数。该条件是任意的,因此它需要一个 enable_if
或类似的东西,但在这个例子中,我只是做一个类型比较。让我们简化它到下面的
- 如果传递一个参数,并且该参数是
int
,returnbool
- 如果传递一个参数并且该参数是
double
,return
int
- 如果传递了多于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>
$
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
- If a single argument is passed and that argument is an
int
, returnbool
- If a single argument is passed and that argument is a
double
, returnint
- 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!
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屋!