我如何根据条件和多少参数启用结构?

时间:2014-10-19 03:34:11

标签: c++ templates

我想创建一个元函数,如果向它传递多个参数,则返回某个类型;如果只传递一个参数,则创建另一个基于条件的类型。条件是任意的,因此它需要enable_if或类似的东西,但是对于这个例子,我只是将它作为类型比较。我们将其简化为以下

  1. 如果传递了一个参数且该参数为int,则返回bool
  2. 如果传递了一个参数且该参数为double,则返回int
  3. 如果传递的参数超过1,请返回double
  4. 为实现这一目标,我试图做到以下几点:

    #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

1 个答案:

答案 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才会通过。