【问题标题】:Operator overloading and different template parameter运算符重载和不同的模板参数
【发布时间】:2014-05-25 12:50:07
【问题描述】:

我在重载 operator+ i 模板类时遇到问题。 假设我有

template<typename T>
struct point{

    //Ctors:
    point(){};
    point(T _x, T _y):x(_x),y(_y){};
    point(const point<T>& p):x(p.x),y(p.y){};

    template<typename T2>
    point(const point<T2>& p):x(static_cast<T>(p.x)),y(static_cast<T>(p.y)){};



   //Coordinates:
   T x;
   T y;


   //Operator overloads:
   friend point<T> operator+(point<T> left,point<T> right ){
       return point<T>(left.x+right.x, left.y+right.y);
   }

   template<class T2>
   friend point<T> operator+(point<T2> left,point<T> right){
       return point<T>(left)+right;

   }

   template<class T3>
   friend point<T> operator+(point<T> left,point<T3> right){
       return point<T>(right)+left;
   }


};

这在调用时给我一个模棱两可的错误。

point<float> p1(1.2,1.4);
point<int> p2(1,2);    
point<float> p3 =p1+p2;    

这是有道理的,但你能告诉我如何解决它的好做法吗?

我需要 3 个操作员,否则演员可能会走错方向。例如,忽略最后一个运算符重载将导致 p3.x=2p3.y=4

非常感谢!

【问题讨论】:

  • 为什么需要3个运算符+s?
  • 编译器不知道选择哪个函数(T2 或 T3)。区别必须在参数中(而不是在返回类型中)。
  • @AdamFolwarczny 我知道这是模棱两可的,但你能告诉我如何解决它。我真的找不到一个很好的解决方案,它的转换基本上取决于返回类型。
  • 表达式模板?
  • 如果浮点数始终具有更高的优先级,则将结果作为浮点数返回。如果你将它赋值给 point 那么它将自动转换:template&lt;class T, class T2&gt; point&lt;float&gt; operator+(point&lt;T2&gt; left, point&lt;T&gt; right){ return point&lt;float&gt;(left) + point&lt;float&gt;(right); }

标签: c++ templates operator-overloading ambiguity


【解决方案1】:

在某种程度上,这是个人偏好和主观意见的问题,但我想我会尝试模拟算术表达式结果类型的普通 C++ 规则,例如

template <class T1, class T2 >
friend
auto operator+( Point<T1> const left, Point<T2> const right )
    -> Point< decltype( left.x + right.x ) > 
{
    return {left.x + right.x, left.y + right.y};
}

同时满足 g++ 4.8.2 和 visual c++ 12.0 的要求比我想象的要复杂,但这里有一些具体的代码可以用这两个编译器干净地编译:

#include <type_traits>
#include <utility>

template< class Number >
struct Point
{
    Number  x;
    Number  y;
};

template< class T1, class T2 >
auto operator+( Point<T1> const left, Point<T2> const right )
    -> Point< typename std::remove_const<decltype( left.x + right.x )>::type > 
{
    using Result_number =
        typename std::remove_const<decltype( left.x + right.x )>::type;
    using Result_point = Point< Result_number >;
    return Result_point{ left.x + right.x, left.y + right.y };
}

auto main()
    -> int
{
    Point<float> const  p1{ 1.2, 1.4 };
    Point<int> const    p2{ 1, 2 };
    Point<float> const  p3 = p1 + p2;
}

为了方便Point 的可能缩小转换,您可以添加显式转换运算符成员函数,如下所示:

template< class Number >
struct Point
{
    Number  x;
    Number  y;

    template< class Other_number >
    explicit
    operator Point<Other_number>() const
    {
        return Point<Other_number>{
            static_cast<Other_number>( x ),
            static_cast<Other_number>( y )
            };
    }
};

要调用使用任何合适的演员表,例如明显的构造函数表示法(上面的定义只是调用转换运算符的强制转换):

    Point<int> const    p4 = Point<int>( p1 + p2 );

【讨论】:

  • +1,即使可能比用户真正需要的更复杂...另外,您是否缺少运算符参数中的引用?
  • 我不确定是否使用引用参数。对于 64 位编程,我认为效率可能会更好地传递值。但我不知道。
  • @Cheersandhth.-Alf 非常感谢!这是很好的代码!不过,还有一个愚蠢的后续行动。在我的代码中使用复制构造函数作为template&lt;typename T2&gt; point(const point&lt;T2&gt;&amp; p):x(static_cast&lt;T&gt;(p.x)),y(static_cast&lt;T&gt;(p.y)){}; 和你的转换函数有什么区别?
  • @user3428018:如上图所示,Point 类是可复制的,因为它没有“非平凡的复制构造函数”(C++11 §9/6 )。除其他外,atomic&lt;T&gt; 的模板参数必须是可简单复制的。主要思想是类的值概念仅源自位模式,因此复制位会产生相同的逻辑值,这一概念体现在 §3.9/3 中。我认为这根本不是很重要,但是只要小心一点,它就会免费提供。例如。不引入构造函数。
  • @Cheersandhth.-我明白了。非常感谢!
【解决方案2】:

如果你想得到结果参数的类型,那么创建方法来计算它:

   template<class T, class T2, class T3>
   void AddPoints(const point<T>& a, const point<T2>& b, point<T3>& result)
   {
       result = point<T3>(a) + point<T3>(b);
   }

int main()
{
    point<float> p1(1.2,1.4);
    point<int> p2(1,2);    
    point<double> result; 
    AddPoints(p1, p2, result);
}

【讨论】:

  • 当然这总是可能的,但是操作符重载似乎更干净更好!有没有办法通过运算符重载来做到这一点?
猜你喜欢
  • 2021-02-17
  • 1970-01-01
  • 2020-02-07
  • 2012-04-26
  • 2016-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多