Dmitry Sapelnikov
Dmitry Sapelnikov

Reputation: 1149

Unified implementation of c++ '+' operator using '+=' operator

For example, I have 2 structures: a 2D vector and a 3D vector with defined += operators:

struct Vector2
{
    double x;
    double y;
    ....
    Vector2& operator+=(const Vector2& vector);
    ....
};

struct Vector3
{
    double x;
    double y;
    double z;
    ....
    Vector3& operator+=(const Vector3& vector);
    ....
};

It is trivial to implement '+' operators for these structures using the corresponding '+=' operators:

Vector2 operator+(const Vector2& vector1, const Vector2& vector2) 
{
    Vector2 result(vector1);
    result += vector2;
    return result;
}

Vector3 operator+(const Vector3& vector1, const Vector3& vector2) 
{
    Vector3 result(vector1);
    result += vector2;
    return result;
}

I'd like to unify these 2 functions and replace them with one template function:

template <class T>
T operator+(const T& vector1, const T& vector2)
{
    T result(vector1);
    result += vector2;
    return result;
}

But this function is so general that it makes operator+ ambiguous for other classes. I tried to make this template applicable only for Vector2 and Vector3 structs using custom type traits and static_assert:

template <class T>
T operator+(const T& vector1, const T& vector2)
{
    static_assert(suitable_type_for_this_function<T>::value, "Unsupported type!");
    ...
}

But it does not hide declaration of the template operator for other classes. Therefore this approach again leads to ambiguity.

How can I implement such a unified operator+, but define it only for these 2 specific types?

Upvotes: 0

Views: 154

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275800

template<class D>
struct add_using_increment {
  using Self=add_using_increment<D>;
  friend D operator+(Self&& lhs, Self const& rhs){
    lhs+=rhs;
    return std::move(lhs.self());
  }
  friend D operator+(Self const& lhs, Self const& rhs){
    return D(lhs.self())+rhs;
  }
private:
  D&self(){ return *static_cast<D*>(this); }
  D const&self()const{ return *static_cast<D const*>(this); }
};

Now just do this:

struct Vector2:add_using_increment<Vector2>

Altenratively you can use SFINAE to restrict your template argument to (A) being one of a fixed set, (B) having a void increment( lhs&, rhs const& ) function defined on it, (C) having a += defined on it.

(C) is too greedy.

You can also use boost.operators which is an industrial strength version of my add_using_increment.

Upvotes: 2

songyuanyao
songyuanyao

Reputation: 172994

You can apply SFINAE, use std::enable_if with std::is_same to constrain the types allowed on T.

template <class T>
std::enable_if_t<std::is_same_v<T, Vector2> || std::is_same_v<T, Vector3>, T>
operator+(const T& vector1, const T& vector2);

Upvotes: 1

Related Questions