sg_man
sg_man

Reputation: 833

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

I have a class template for a 3d vector class that looks kind of like (abbreviated):

template<typename T>
class vector3
{
public:

  typedef vector3<T> self;

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

};

Both of the operator* overloads 'self multiply' the vector times a scalar, and return *this.

I want to be able to use the class template like this:

vector3<double> vv;
double scalar;

vv*scalar;
vv*0.5;

The overload ambiguity is clear, and 'just get it to work' kind of solutions are available and have been discussed in other SA questions.

If you remove operator*(T& a), everything will compile, but you (at least in theory), lose some performance benefit from passing by value when you don't really need to (right?)

If you remove operator*(T a), you can't do vv*0.5.

If you rename one of them, you lose a lot of code clarity in cases where all these operations intuitively make sense, math-wise.

Is there any way to retain the pass by reference when it makes sense, but remove the overload ambiguity? What's the best way to make sure the vector3 template accommodates both expressions above?

Upvotes: 1

Views: 218

Answers (3)

J&#233;r&#244;me
J&#233;r&#244;me

Reputation: 8066

You could create a method that takes an rvalue as parameter as Barry described.

Or your template parameter could be const if the implementation does not modify it. In this case you could have only one method taking a const reference:

self operator*(const T& a) const;

Upvotes: 0

Andrey Nasonov
Andrey Nasonov

Reputation: 2629

The best choice for you is

self operator * (const T& a) const;

There are three moments:

  1. Using const T& instead of T or T&. Modern compilers are smart, they will optimize the code and pass small types by value instead of reference.

  2. The operator * should not change the operands.

  3. The operator * should return a value but not a reference.

Upvotes: 3

Barry
Barry

Reputation: 303137

Just make the other one take an rvalue reference:

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

Although, do you really have a different implementation for operator* for the two different cases? You probably just want:

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

Note that operator* should return a value. operator*= should return a reference.

Upvotes: 0

Related Questions