c++ - 如何根据条件和参数数量启用结构体?

标签 c++ templates

我想创建一个元函数,如果传递给它的参数超过 1 个,它返回特定类型,如果只传递一个参数,则返回基于条件的另一种类型。条件是任意的,所以它需要一个 enable_if 或类似的东西,但对于这个例子,我只是让它成为一个类型比较。让我们将其简化为以下内容

  1. 如果传递了一个参数并且该参数是一个int,返回bool
  2. 如果传递了一个参数并且该参数是一个double,返回int
  3. 如果传递了超过 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, "");

我很感激能告诉我为什么这不像我预期的那样工作,而不仅仅是如何解决它。我正在努力寻找有关模板元编程的好资源,并且到目前为止一直在相当随意地编程,这是我非常想解决的问题!

Live example here

最佳答案

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/

相关文章:

c++ - visual c++ 复制文本框内容

c++ - 将 lambda 隐式转换为 boost::function

c++ - SFINAE: std::enable_if 作为函数参数

c++ - 在 C++ 中使用内存屏障防止凭空值

c++ - 我无法初始化 WCHAR

C++ - 这个单独的编译代码有什么问题?

c++ - 模板特化语法错误​​?没有把握

templates - 轨道 3 : @template variable inside controllers is nil

javascript - 快速路由模板

c++ - const 迭代器的模板参数而不是迭代器