【问题标题】:Forward integral argument to one constructor, floatingpoint to another将整数参数转发给一个构造函数,将浮点数转发给另一个
【发布时间】:2014-12-03 16:00:53
【问题描述】:

我有:

class C 
{
    C(long){...};
    C(double){...};
    :
}

很遗憾,

C c{5}; // error! ambiguous overload

(这很可怕,不是吗?整数类型肯定有利于构造函数采用更高精度的整数参数。)

如何正确地将整数参数和浮点参数正确转发给各自的构造函数?

编辑:也许我把问题简单化了。它最初来自this enquiry。我正在包装 Python 原语,例如 Float Long String,重要的是初始化被转发到正确的原语。同时,由于这是一个通用的包装器,我不希望消费者为了避免内部陷阱而担心类型转换。

正如 Mike Seymour 所指出的,SFINAE 提供了一种处理此问题的技术。

非常感谢 FreeNode C++ 频道上的 doug64k 提供以下解决方案:

http://ideone.com/QLUpu2 http://ideone.com/TCigR3 http://ideone.com/oDOSLH

我会在明天找到线索时尝试将这些作为答案。

【问题讨论】:

  • 你让编译器猜测你是否忘记了 5 之后的 L 或 .0。我非常喜欢不需要编译器猜测的代码和不需要猜测的编译器.
  • @gnasher729,我尽量保持简单。 C{someInt} 怎么样?
  • @Pi:我只需要C{long(someInt)}C{double(someInt)},这取决于调用者想要的。试图打破该语言的类型转换规则绝非易事(尽管通过一些可怕的 SFINAE 使用,您几乎可以得到您认为想要的东西)。
  • @MikeSeymour 为什么可怕? stackoverflow.com/a/14603258/4224575

标签: c++ templates c++11 constructor-overloading


【解决方案1】:

如何正确地将整数参数和浮点参数正确转发给各自的构造函数?

用途:

C c1{5L};
C c2{5.0};

这太可怕了,不是吗?整数类型肯定有利于构造函数采用更高精度的整数参数

标准遵循一种算法来决定哪个重载函数是最佳匹配。除其他外,它对“促销”的排名高于“转化”。也就是说,“积分提升”的排名高于“积分转换”,“浮点提升”的排名高于“浮点转换”。但是,“积分转换”的排名不高于“浮点转换”这是13.3.3.1.1 标准转换序列

部分中的表格

4 标准转换下可以使用“积分提升”、“积分转换”、“浮点提升”和“浮点转换”的标准地址。就本答案而言,只需说int 可以转换long 不提升int 也可以转换double。这就解释了为什么当参数类型是 int 时编译器无法消除重载之间的歧义。

【讨论】:

    【解决方案2】:

    我只是让它保持原样,让用户明确说明需要哪种类型转换。如果您真的想根据您描述的非标准规则允许隐式转换,您可以使用 SFINAE。这样的事情会起作用:

    #include <iostream>
    #include <type_traits>
    
    struct C {
        // Constructor for integer types
        template <typename T>
        C(T, typename std::enable_if<std::is_integral<T>::value, T>::type=0)
            {std::cout << "integral\n";}
    
        // Constructor for floating-point types
        template <typename T>
        C(T, typename std::enable_if<std::is_floating_point<T>::value, T>::type=0)
            {std::cout << "floating\n";}
    };
    
    int main() {
        C c1{5};   // prints "integral"
        C c2{5.0}; // prints "floating"
    }
    

    这种胡言乱语,以及那些习惯于通常的隐式转换规则的人的困惑,是否值得在显式转换中保存,这是一个见仁见智的问题。

    【讨论】:

      【解决方案3】:

      您可以使用模板构造函数将未声明的类型重定向到您选择的构造函数。

      假设您希望 long 成为您的默认值。使用 SFINAE,您可以检查类型 T 是否能够转换为 long,然后将其传递给长构造函数!

      class C 
      {
      public:
          C(long l){ std::cout << "long constructor" << std::endl; };
          C(double d){std::cout << "double constructor" << std::endl; };
      
          // default constructor which passes values to long
          template <typename T, 
             typename std::enable_if<std::is_convertible<long, T>::value, int>::type = 0>
          C(T t) : C(long(t)){};
      };
      
      
      int main() {
          C c1(5);
          C c2(5.0f);
          C c3(5.0L);
          C c4(5.0);
          return 0;
      }
      

      这个输出:

      long constructor
      long constructor
      long constructor
      double constructor
      

      【讨论】:

        猜你喜欢
        • 2011-04-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-05
        • 2021-11-20
        • 2014-01-12
        相关资源
        最近更新 更多