[英]Can refactoring an overloaded operator into a non-member function break any code?
考虑具有重载加法运算符+=
和+
的遗留类模板
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
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.