我想创建一个元函数,如果向它传递多个参数,则返回某个类型;如果只传递一个参数,则创建另一个基于条件的类型。条件是任意的,因此它需要enable_if
或类似的东西,但是对于这个例子,我只是将它作为类型比较。我们将其简化为以下
int
,则返回bool
double
,则返回int
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, "");
我很感激一个能够教会我的答案为什么这并不像我预期的那样工作,而不仅仅是如何修复它。我很难在模板元编程上找到好的资源,并且到目前为止编程已经非常随意,这是我非常想解决的问题!
答案 0 :(得分:6)
专业化的template
参数不是原始定义的模板参数。
与原始定义相对应的名称位于template
名称之后。
专业化的template<>
部分中的参数是为匹配专业而引入的类型。
原来的定义:
template <typename Enable, typename...Args>
struct Get;
一个或多个类型参数。并且,除非专业化匹配,否则未定义。
第一次专业化:
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 1}}。
以下是一个例子:
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
才会通过。