I have an incredibly exciting library that can translate points: it should work with any point types
template<class T>
auto translate_point(T &p, int x, int y) -> decltype(p.x, p.y, void())
{
p.x += x;
p.y += y;
}
template<class T>
auto translate_point(T &p, int x, int y) -> decltype(p[0], void())
{
p[0] += x;
p[1] += y;
}
translate_point
will work with points that have public x
and y
members, and it will also work with tuples/indexable containers where x
and y
are represented by the first and second element, respectively.
The problem is, another library defines a point class with public x
and y
, but also allows indexing:
struct StupidPoint
{
int x, y;
int operator[](int i) const
{
if(i == 0) return x;
else if(i == 1) return y;
else throw "you're terrible";
}
};
My application, using both libraries, is the following:
int main(int argc, char **argv)
{
StupidPoint stupid { 8, 3 };
translate_point(stupid, 5, 2);
return EXIT_SUCCESS;
}
but this makes GCC (and clang) unhappy:
error: call of overloaded ‘translate_point(StupidPoint&, int, int)’ is ambiguous
Now I can see why this is happening, but I want to know how to fix this (assuming I can't change the internals of StupidPoint), and, if there is no easy workaround, how I might as a library implementer make this easier to deal with.
There are two ways to resolve this ambiguity: Typecast char to float. Remove either one of the ambiguity generating functions float or double and add overloaded function with an int type parameter.
If the compiler can not choose a function amongst two or more overloaded functions, the situation is -” Ambiguity in Function Overloading”. The reason behind the ambiguity in above code is that the floating literals 3.5 and 5.6 are actually treated as double by the compiler.
When the compiler is unable to decide which function it should invoke first among the overloaded functions, this situation is known as function overloading ambiguity. The compiler does not run the program if it shows ambiguity error.
If you want to give precedence to the case having public x
/y
, you can do this:
template<class T>
auto translate_point_impl(int, T &p, int x, int y) -> decltype(p.x, p.y, void())
{
p.x += x;
p.y += y;
}
template<class T>
auto translate_point_impl(char, T &p, int x, int y) -> decltype(p[0], void())
{
p[0] += x;
p[1] += y;
}
template<class T>
void translate_point(T &p, int x, int y) {
translate_point_impl(0, p, x, y);
}
It goes without saying that the opposite configuration is given by switching the types of the first parameter.
If you have three or more options (says N
), you can use a trick based on templates.
Here is the example above once switched to such a structure:
template<std::size_t N>
struct choice: choice<N-1> {};
template<>
struct choice<0> {};
template<class T>
auto translate_point_impl(choice<1>, T &p, int x, int y) -> decltype(p.x, p.y, void()) {
p.x += x; p.y += y;
}
template<class T>
auto translate_point_impl(choice<0>, T &p, int x, int y) -> decltype(p[0], void()) {
p[0] += x;
p[1] += y;
}
template<class T>
void translate_point(T &p, int x, int y) {
// use choice<N> as first argument
translate_point_impl(choice<1>{}, p, x, y);
}
As you can see, now N
can assume any value.
You could provide an overload for StupidPoint
:
auto translate_point(StupidPoint &p, int x, int y)
{
p.x += x;
p.y += y;
}
live example
Another solution:
Since operator[]
is const for StupidPoint
, you can check this in your SFINAE condition:
template<class T>
auto translate_point(T &p, int x, int y) -> decltype(p[0] += 0, void())
{
p[0] += x;
p[1] += y;
}
live example
You could also use a different type-traits based approach to select the appropriate translate_point
function:
template<typename T, typename = void>
struct has_x_y : std::false_type { };
template<typename T>
struct has_x_y<T, decltype(std::declval<T>().x, std::declval<T>().y, void())> : std::true_type { };
template<typename T, typename = void>
struct has_index : std::false_type { };
template<typename T>
struct has_index<T, decltype(std::declval<T>().operator[](0), void())> : std::true_type { };
template<class T>
std::enable_if_t<has_x_y<T>::value> translate_point(T &p, int x, int y)
{
p.x += x;
p.y += y;
}
template<class T>
std::enable_if_t<!has_x_y<T>::value && has_index<T>::value> translate_point(T &p, int x, int y)
{
p[0] += x;
p[1] += y;
}
live example
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With