C++中按引用传递和按值传递之间重载歧义的实用解决方案

Practial solution to overload ambiguity between pass by reference and pass by value in C++

我有一个 class 3d 矢量模板 class 看起来有点像(缩写):

template<typename T>
class vector3
{
public:

  typedef vector3<T> self;

  self& operator*(T& a);
  self& operator*(T a);
  T x,y,z;

};

两个 operator* 重载 'self multiply' 向量乘以标量,return *this.

我希望能够像这样使用 class 模板:

vector3<double> vv;
double scalar;

vv*scalar;
vv*0.5;

重载歧义很明显,并且'just get it to work'种解决方案可用并且已在其他SA问题中讨论过。

如果你删除 operator*(T& a),一切都会编译,但是你(至少在理论上),当你真的不需要时,你会失去一些通过值传递的性能优势(对吗?)

如果去掉operator*(T a),就做不到vv*0.5.

如果您重命名其中一个,那么在所有这些操作在数学方面直观上有意义的情况下,您会失去很多代码清晰度。

有没有办法在有意义的时候保留按引用传递,但消除重载歧义?确保 vector3 模板适应上述两个表达式的最佳方法是什么?

只需让另一个采用 rvalue 参考:

self operator*(T& a);   // for 'scalar'
self operator*(T&& a);  // for 0.5

尽管如此,对于这两种不同的情况,您真的对 operator* 有不同的实现吗?你可能只想:

template <typename U,
          typename = std::enable_if_t<std::is_same<std::decay_t<U>, T>::value>>
self operator*(U&& a);  // for both

请注意 operator* 应该 return 一个值。 operator*= 应该 return 参考。

最适合您的选择是

self operator * (const T& a) const;

一共有三个瞬间:

  1. 使用 const T& 而不是 TT&。现代编译器很聪明,他们会优化代码并按值而不是引用传递小类型。

  2. 运算符*不应更改操作数。

  3. 运算符 * 应该 return 一个值而不是一个引用。

您可以创建一个将右值作为参数的方法,如 Barry 所述。

或者您的模板参数可以是 const 如果实现不修改它。在这种情况下,您可能只有一种方法采用 const 参考:

self operator*(const T& a) const;