Reputation: 2686
I am looking for a way to express interoperability between a class A
and built-in integer types while keeping high flexibility in my code. E.g, I would like to be able to use operator &
freely between (A
and A
), (A
and int
), (int
and A
) and (int
and int
), i.e. I want to have the result of x = y & z
whether x
, y
and z
are type class A
or type int
, just writing:
x = y & z;
The following code works:
#include <cstdlib>
#include <iostream>
class A {
public:
int x;
explicit A(int i) : x(i) {}
operator int() {
return this->x;
}
A operator &(const A& src) const {
return A(this->x & src.x);
}
};
int main() {
int b(2), b2(0), b3(0);
A a(3);
b2 = a & b;
b3 = b & a;
std::cout << b2 << std::endl;
std::cout << b3 << std::endl;
return 0;
}
However, if I add a new cast function from A
to unsigned int
in class A
, this does not work any more because operator &
is defined between (int
and int
) and also (int
and unsigned int
), so when I do:
b2 = a & b
the compiler doesn't know if a
should be cast to int
or unsigned int
, which is logical. I see 2 possibilities to solve it:
operator &
between A
and int
and between int
and A
. I don't want that because adding the compatibility with another type would require to re-implement many combinations of all operators which need to be supported.A
, so only operator &
between A
and A
is required.For flexibility and maintainability, solution 2 is much better I think. So I can implement the following class A instead:
class A {
public:
int x;
A(int i) : x(i) {}
A(unsigned int i) : x(i) {}
explicit operator int() {
return this->x;
}
explicit operator unsigned int() {
return static_cast<unsigned int>(this->x);
}
};
A operator &(const A& src1, const A& src2) {
return A(src1.x & src2.x);
}
Now, though conversions from/to int and unsigned int are both defined, I can perform whether (A
and A
), (A
and int
), (int
and A
) and (int
and int
).
However I can't compile the code:
b2 = a & b;
b3 = b & a;
Because as b2
and b3
are int
and (a
& b
) (resp. (b
& a
)) return a A
and cast from A
to int
must now be explicit, I have to write:
b2 = static_cast<int>(a & b);
b3 = static_cast<int>(b & a);
My question (finally) is:
Is there a way to code class A
so I can do:
b2 = a & b;
b3 = b & a;
while keeping only one definition of operator &
, between (A
and A
)? In theory, that could be done overloading operator =(const A&)
of class int
, which is technically impossible.
Upvotes: 2
Views: 58
Reputation: 2686
I think I have just come across a solution. Please consider the following code:
class A {
public:
int x;
explicit A(int i) :
x(i) {
}
explicit A(unsigned int i) :
x(i) {
}
operator int() {
return this->x;
}
operator unsigned int() {
return static_cast<unsigned int>(this->x);
}
};
template<typename T> A operator &(const A& src1, const T& src2) {
return A(src1.x & src2);
}
template<typename T> A operator &(const T& src1, const A& src2) {
return A(src1 & src2.x);
}
int main() {
int b = 2, b2 = 0, b3 = 0;
A a(3);
b2 = a & b;
b3 = b & a;
std::cout << b2 << std::endl;
std::cout << b3 << std::endl;
return 0;
}
It just happens to work. The only problem I see (which is quite important though) is that you can't control the effects of operator &
with built-in types you have not considered.
New question then:
Is there a way to restrict my template operator &
to a given list of types (without template specialization)?
Upvotes: 0
Reputation: 5321
I think bipll means using free-standing operator&
functions:
#include <cstdlib>
#include <iostream>
using std::cout;
using std::endl;
class A
{
int x;
public:
explicit A(int i) : x{i}
{ }
explicit A(unsigned i) : x{static_cast<int>(i)}
{ }
operator int() const
{
return this->x;
}
operator unsigned() const
{
return static_cast<unsigned>(this->x);
}
};
A operator&(A const& lhs, A const& rhs)
{
return A(lhs.operator int() & rhs.operator int());
}
A operator&(A const& lhs, int rhs)
{
return A(lhs.operator int() & rhs);
}
A operator&(int lhs, A const& rhs)
{
return A(lhs & rhs.operator int());
}
A operator&(A const& lhs, unsigned rhs)
{
return A(lhs.operator unsigned() & rhs);
}
A operator&(unsigned lhs, A const& rhs)
{
return A(lhs & rhs.operator unsigned());
}
int main()
{
auto b = 2;
auto b2 = 0;
auto b3 = 0;
auto u = 2;
auto u4 = 0u;
auto u5 = 0u;
auto a = A{3};
b2 = a & b;
b3 = b & a;
u4 = a & u;
u5 = u & a;
cout << b2 << endl;
cout << b3 << endl;
cout << u4 << endl;
cout << u5 << endl;
}
Upvotes: 1