【问题标题】:With templates, how to differentiate two parallel cases, such as between floating point types and integral types?使用模板,如何区分两种并行的情况,比如浮点类型和整数类型?
【发布时间】:2016-11-10 20:47:30
【问题描述】:

cppreference.com (http://en.cppreference.com/w/cpp/types/enable_if#Notes) 指出:

一个常见的错误是声明两个仅在默认模板参数上有所不同的函数模板。这是非法的,因为默认模板参数不是函数模板签名的一部分,并且使用相同的签名声明两个不同的函数模板是非法的。

struct T {
    enum { int_t,float_t } m_type;
    template <typename Integer,
              typename = std::enable_if_t<std::is_integral<Integer>::value>
    >
    T(Integer) : m_type(int_t) {}
 
    template <typename Floating,
              typename = std::enable_if_t<std::is_floating_point<Floating>::value>
    >
    T(Floating) : m_type(float_t) {} // error: cannot overload
};

确实如此……那么解决这个问题的正确方法是什么,并实际实现上述错误代码未能实现的目标?

【问题讨论】:

    标签: c++ templates sfinae


    【解决方案1】:

    我相信这会奏效:

    #include <type_traits>
    
    struct T {
        enum { int_t,float_t } m_type;
        template <typename Integer,
                  std::enable_if_t<std::is_integral<Integer>::value>* = nullptr
        >
        T(Integer) : m_type(int_t) {}
    
        template <typename Floating,
                  std::enable_if_t<std::is_floating_point<Floating>::value>* = nullptr
        >
        T(Floating) : m_type(float_t) {} // now the overload is valid
    };
    
    int main() {
        T t = int{};
        T t2 = float{};
        (void)t;
        (void)t2;
    }
    

    [live demo]

    【讨论】:

      【解决方案2】:

      使用标签调度:

      namespace tag
      {
      struct floating{};
      struct integer{};
      struct error{};
      
      template<typename T, typename = void> struct get : error {};
      
      template<typename T>
      struct get<T, std::enable_if_t<std::is_integral<T>::value>> : integer {};
      
      template<typename T>
      struct get<T, std::enable_if_t<std::is_floating_point<T>::value>> : floating {};
      
      }
      
      struct Foo
      {
          template<typename T>
          Foo(T&&, tag::floating){
          }
      
          template<typename T>
          Foo(T&&, tag::integer){
          }
      
          template<typename T>
          Foo(T&& t) : Foo(std::forward<T>(t), tag::get<std::decay_t<T>>{}) {}
      };
      

      demo

      【讨论】:

      • 如果每次调用方法时,我们都必须手动传递一个标签,告诉 C++ float 是浮点类型,int 是整数类型,这似乎很尴尬……不会让 C++ 自己解决这个问题会更好吗?
      • @gaazkam 嗯,它是 C++ 中一个成熟且非常灵活的习惯用法(看看iterator_traits)。例如,您可以使用重载解决机制来选择您想要的重载,如果您将标签结构化为层次结构,则可以选择回退到更通用的重载(同样,您可以为 random_iterator_tagbidirectional_iterator_tag 提供重载)。如果您想支持另一组类型,那么扩展这种方法也比使用启用 if 嵌套要容易得多。
      【解决方案3】:

      在构造函数参数列表的另一个虚拟参数中使用enable_if

      struct T {
          enum { int_t,float_t } m_type;
      
          template <typename Integer>
          T(Integer, std::enable_if_t<std::is_integral<Integer>::value>* = nullptr)
              : m_type(int_t) {}
      
          template <typename Floating>
          T(Floating, std::enable_if_t<std::is_floating_point<Floating>::value>* = nullptr)
              : m_type(float_t) {}
      };
      

      【讨论】:

        【解决方案4】:

        我相信我已经找到了另一种解决方法。遗憾的是,由于模板中使用浮点类型的限制,这不适用于浮点数,但应该适用于大多数其他情况(例如区分有符号和无符号类型):

        struct T {
            enum { signed_t,unsigned_t } m_type;
            template <typename Signed,
                      std::enable_if_t<std::is_signed<Signed>::value, bool> = true
            >
            T(Signed) : m_type(signed_t) {}
        
            template <typename Unsigned,
                      std::enable_if_t<std::is_unsigned<Unsigned>::value, bool> = true
            >
            T(Unsigned) : m_type(unsigned_t) {}
        };
        

        现场示例:http://ideone.com/xqp4nd

        【讨论】:

        • 您不必为std::enable_if_t 使用不同的返回类型。
        • @Jarod42 错误,否则我会陷入我的问题中所述的问题,不是吗?
        • 不,即使你删除了默认值,你仍然有不同的类型:std::enable_if_t&lt;std::is_signed&lt;T&gt;::value&gt; vs std::enable_if_t&lt;std::is_unsigned&lt;T&gt;::value&gt;
        • @Jarod42 现在好点了吗?不过,我认为我不能保留默认的void
        猜你喜欢
        • 1970-01-01
        • 2018-08-04
        • 2014-03-25
        • 2022-01-27
        • 1970-01-01
        • 1970-01-01
        • 2020-02-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多