我想创建一个元函数,如果传递给它的参数超过 1 个,它返回特定类型,如果只传递一个参数,则返回基于条件的另一种类型。条件是任意的,所以它需要一个 enable_if
或类似的东西,但对于这个例子,我只是让它成为一个类型比较。让我们将其简化为以下内容
- 如果传递了一个参数并且该参数是一个
int
,返回bool
- 如果传递了一个参数并且该参数是一个
double
,返回int
- 如果传递了超过 1 个参数,则返回
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, "");
我很感激能告诉我为什么这不像我预期的那样工作,而不仅仅是如何解决它。我正在努力寻找有关模板元编程的好资源,并且到目前为止一直在相当随意地编程,这是我非常想解决的问题!
最佳答案
template
特化的参数不是原始定义的模板参数。
原来定义对应的在template
之后姓名。
template<>
中的参数特化的一部分是为匹配特化而引入的类型。
所以原始定义:
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
子句产生一个类型。在生成所有类型(模式和非模式)之后,生成的类型列表要么匹配要么不匹配。
使用此机制的简单方法包括将您的公共(public)版本转发到详细版本,传递 void
作为第一种,做你的enable_if
在那里工作。
另一种方法是将类型捆绑到类型列表中,例如 template<class...>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 个或多个附加类型(用于任意一组测试)。
关于c++ - 如何根据条件和参数数量启用结构体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26447026/