c++ - 将重载运算符重构为非成员函数是否会破坏任何代码?

标签 c++ operator-overloading implicit-conversion argument-dependent-lookup non-member-functions

考虑一个带有重载加法运算符 +=+ 的遗留类模板>

template<class T>
class X
{
public:
    X() = default;
    /* implicict */ X(T v): val(v) {}

    X<T>& operator+=(X<T> const& rhs)       { val += rhs.val; return *this; }
    X<T>  operator+ (X<T> const& rhs) const { return X<T>(*this) += rhs;    } 

private:
    T val;
};

在代码审查中,观察到 + 可以根据 += 实现,所以为什么不将其设为非成员(并保证左对称)和正确的论点)?

template<class T>
class X
{
public:
    X() = default;
    /* implicit */ X(T v): val(v) {}

    X<T>& operator+=(X<T> const& rhs)       { val += rhs.val; return *this; }

private:
    T val;
}; 

template<class T>
X<T> operator+(X<T> const& lhs, X<T> const& rhs)
{ 
    return X<T>(lhs) += rhs; 
}

它看起来足够安全,因为所有使用 ++= 的有效表达式都保留了它们的原始语义。

问题:将operator+ 从成员函数重构为非成员函数是否会破坏任何代码?

破损的定义(从最差到最好)

  • 将编译在旧场景下未编译的新代码
  • 在旧场景下编译的旧代码将无法编译
  • 新代码将静默调用不同的operator+(来自基类或通过 ADL 拖入的关联命名空间)

最佳答案

总结

答案是肯定的,总会有破损的。基本要素是函数模板参数推导不考虑隐式转换。我们考虑三种情况,涵盖重载运算符可以采用的三种语法形式。

这里我们在 X<T> 中使用了一个隐式构造函数本身。但即使我们创建了构造函数 explicit , 用户可以添加到 X<T> 的命名空间一类C<T>包含 operator X<T>() const 形式的隐式转换.在这种情况下,以下情况将继续成立。

从某种意义上说,非成员友元函数破坏最少,因为它允许 lhs 参数隐式转换,而这种转换不会为类模板的成员函数编译。非成员函数模板破坏了 rhs 参数的隐式转换。

类模板的成员函数

template<class T>
class X
{
public:
    /* implicit */ X(T val) { /* bla */ }
//...
    X<T> operator+(X<T> const& rhs) { /* bla */ }
//...
};

这段代码将允许像这样的表达

T t;
X<T> x;
x + t;  // OK, implicit conversion on non-deduced rhs
t + x;  // ERROR, no implicit conversion on deduced this pointer

非成员(member)好友功能

template<class T>
class X
{
public:
    /* implicit */ X(T val) { /* bla */ }
//...
    friend 
    X<T> operator+(X<T> const& lhs, X<T> const& rhs) { /* bla */ }
//...
};

friend函数不是模板,不会进行参数推导,并且 lhs 和 rhs 参数都考虑隐式转换

T t;
X<T> x;
x + t;  // OK, implicit conversion on rhs
t + x;  // OK, implicit conversion on lhs

非成员函数模板

template<class T>
class X
{
public:
    /* implicit */ X(T val) { /* bla */ }
//...
};

template<class T> 
X<T> operator+(X<T> const& lhs, X<T> const& rhs) { /* bla */ }

在这种情况下,lhs 和 rhs 参数都进行参数推导,并且都不考虑隐式转换:

T t;
X<T> x;
x + t;  // ERROR, no implicit conversion on rhs
t + x;  // ERROR, no implicit conversion on lhs

关于c++ - 将重载运算符重构为非成员函数是否会破坏任何代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26089156/

相关文章:

c++ - 无法从另一个模板类调用模板类的模板成员函数

c++ - Linux下的Qt资源

C++ 生成器-Piece.cpp(20) : E2316 'Button1Click' is not a member of 'TForm'

c++ - 无法在 C++ 中重载输出流

Java:隐式类型转换,或隐式 toString() 调用

scala - scala 确保 block 中的隐式转换失败

c++ - 如何删除添加到 C++ 链表中的最后一个元素

java - 运算符重载和JVM处理之间的联系

c++ - 为什么不使用强制转换语法调用 "operator void"?

c++ - 为什么显式运算符 bool 让我转换为任何原始类型?