0

When I made my vector3 struct I noticed the operator functions were almost identical to eachother


vector3 vector3::operator+(float scalar) const {
    return vector3(x + scalar, y + scalar, z + scalar);
}

// And…

vector3 vector3::operator*(float scalar) const {
    return vector3(x * scalar, y * scalar, z * scalar);
}

I was able to copy the code from above and replace the + symbol to a *.

Is there any way of writing both of those functions in one so the overloads aren’t duplicated each time for each operator.

Something like using template where I could define each operator in a sequence, and replace the + or the * in the previous code with a name.

template <operatorname o>
vector3 vector3::operator o(float scalar) const {
    return vector3(x o scalar, y o scalar, z o scalar);
}

1 Answer 1

0

You can write a function template that applies an operation to all elements one by one:

template <typename F>
void apply(vector3& vec, F&& f) {
      f(vec.x);
      f(vec.y);
      f(vec.z);
}

Then you can implement operator+= as

vector3::operator+=(float scalar) { 
     apply(*this,[](auto& a) { a += scalar; });
}

Note that I started from compound operators, as you can implement the non-compound operators in terms of them.

You can see that this is actually not any less to type than spelling it out.

I actually think you are overthinking this. Calling a function with different arguments is not what we typically consider code duplication that needs to be avoided. Your implementation is as clear as it can get. To multiply a vector with a scalar you multiply each component with the scalar. Your code expresses that, in a readable way, it has nothing superfluous that could be removed. I also see no way to write less boilerplate, add a general helper, then specialize it for the different operators is adding boilerplate not removing it.


Your desired syntax cannot be realized like that, but there is something that comes closer to it than what I wrote above. You can write the helper

template <typename F>
void apply(vector3& vec,float scalar, F&& f) {
     vec.x = f(res.x,scalar);
     vec.y = f(res.y,scalar);
     vec.z = f(res.z,scalar);
}

And then you can make use of the standard functors, eg std::multiplies to implement the operators, eg

vector3::operator*=(float scalar) {
     apply(*this,scalar,std::multiplies{});
}
7
  • Thank you, but it would be less to type as I have 20 different operator overloads. Also: To do “operator” on a vector with a scalar you use “operator” with each component with the scalar. It can be described the same way just more abstractly. It’s ok if there is no syntax like this, but it would still make sense. Commented Apr 25 at 16:35
  • how come you have 20 different element wise operators? You can implement the non-compound ones in terms of the compound ones. - uses +, / can use *. On the other hand dot product and scalar product are so special that you can hardly generalize them (or you have the elements in containers, then you can use standard algorithms). Commented Apr 25 at 16:40
  • I have different operators for the non-compound operators, I would still be able to use my technique if it was possible and I wanted to reimplement them with the compound variant Commented Apr 25 at 17:14
  • @enieslobby oh well, I think i only now actually understand the question. You want to write 1 template <operatorname o> vector3 vector3::operator o and then that would be sufficient to have any operator usable without writing any further code? No, thats not possible. Templates dont work like that. Commented Apr 25 at 17:17
  • Yes thank you, it’s only a silly dream then. Maybe I’ll make my own language with this feature /hj Commented Apr 25 at 17:37

Not the answer you're looking for? Browse other questions tagged or ask your own question.