Suppose I have a class A, and I define an overload of an operator, acting on a class of the standard library. To have a concrete example, see following code:
#include <iostream>
#include <algorithm>
#include <stdexcept>
#include <vector>
template <typename T>
std::vector<T>& operator+=(std::vector<T>& lhs, const std::vector<T>& rhs) {
if (lhs.size() != rhs.size())
throw(std::runtime_error("incompatible vectors"));
std::transform(rhs.begin(), rhs.end(), lhs.begin(), lhs.begin(), [](const auto& x, const auto& y) { return x + y; });
return lhs;
}
template <typename T>
struct A {
std::vector<T> v;
A(const T& x) : v(3, x) { }
void add (const std::vector<T>& rhs) { v += rhs; }
};
int main()
{
A<int> a(1);
std::vector<int> bvec(3, 2);
a.add(bvec);
return 0;
}
Check it live on Coliru.
The code works as expected. However, I would like to "hide" the overload of operator+=
, such that it can be only used by struct A
.
The reason is that, since operator +=
is a template one, I would need to insert it into a header, to be included whenever I need struct A
in a compilation unit: this could potentially generates conflicts, for example if the compilation unit already has defined an operator+=
for std::vector
.
One way to "hide" it could be wrap everything inside a namespace
:
namespace code {
template <typename T>
std::vector<T>& operator+=(std::vector<T>& lhs, const std::vector<T>& rhs) {
// ....
template <typename T>
struct A {
// ....
}
This seems a bit cumbersome, and again, if the code contains using namespace code
, which I cannot prevent, it could still generate conflicts. Moreover, every other routines inserted inside the namespace code
will be able to access to operator+=
, again with potential conflicts.
Another solution could be to hide the operation inside the definition of struct A
:
#include <iostream>
#include <algorithm>
#include <stdexcept>
#include <vector>
template <typename T>
struct A {
private:
template <typename U>
void do_add(const std::vector<U>& rhs) {
if (v.size() != rhs.size())
throw(std::runtime_error("incompatible vectors"));
std::transform(rhs.begin(), rhs.end(), v.begin(), v.begin(), [](const auto& x, const auto& y) { return x + y; });
}
public:
std::vector<T> v;
A(const T& x) : v(3, x) { }
void add (const std::vector<T>& rhs) { do_add(rhs); }
};
int main()
{
A<int> a(1);
std::vector<int> bvec(3, 2);
a.add(bvec);
return 0;
}
(Obviously I could directly implement everything inside A::add
, the above code is for illustration only-- in a real case several methods of A
may need to call do_all
, hence a separate private implementation).
The drawback of this solution is that it "ruins" a bit the readability of the code: now I have to call a routine do_add
for doing something which is "logically" an operator+=
.
Ideally, I would prefer to write something like
template <typename T>
struct A {
private:
std::vector<T>& operator+=(std::vector<T>& lhs, const std::vector<T>& rhs);
// ...
}
that is, defining an operator+=
inside the private part of struct A
. In this way I would use the clear syntax v += rhs
, and still restrict the use of operator+=
to the code inside struct A
.
Alas, the code above cannot compile, because the compiler expects that operator+=
inside struct A
deals with objects of type A
...
If the member v
would be a user-defined type X
, then I could indeed insert operator+=
in the definition of X
. But again, not for std classes. And, deriving from the std is UB.
Then, a third solution would be to wrap std::vector
inside a custom class
template <typename T>
class vec_wrapper {
vec_wrapper<T>& operator+=(const vec_wrapper& rhs);
std::vector<T>& m_data;
//...
}
But this is extremly cumbersome: If I then want to use my vec_wrapper
as usual std::vector
I would need to provide a duplicate of all std::vector
member functions, say T& operator[](std::size_t n) { return m_data[n]; }
, etc...
So, the question is: is there a way to mantain the "nicely" defined overload of operator+=
, but restrict its use to struct A
?