dariobaron
dariobaron

Reputation: 45

Compiler throws "ambiguous overload for operator"

I'm learning how to use std::chrono and I want to make a template class Timer easy to use (defined in timer.h). The testing program was successful and everything worked fine, until I tried to use my new Timer in a program with the definition of some template operators, which conflit with the operators used inside Timer.

Inside Timer I have to use operator- between two variables (start_time and end_time) of type std::chrono::time_point, in order to obtain the duration variable containing the elapsed time.

In another header (algebra.h) I implemented the overloading of the binary operator- to make the difference between two std::vector or two std::array, or also a user-defined container provided with operator[] and size() member function.

template<typename pointType>
pointType operator-(pointType a, const pointType & b){
    for(int i = 0; i < a.size(); ++i){
        a[i] = a[i] - b[i];
    }
    return a;
}

When I try to include both timer.h and algebra.h, the compiler throws an error saying "ambiguous overload for operator-" suggesting, as possible candidates, both the operator in algebra.h and the one implemented in <chrono>.

I don't understand why it is ambiguous, since pointType can't be deduced as std::chrono::time_point because it doesn't have operator[] and size() member function.

P.S. I tried something else to work it out, but I only got more confused testing a program which use std::valarray. When I include both <valarray> and "algebra.h", and try to make a difference between two valarrays, I expected the compiler to complain about ambiguous definition of operator-, since std::valarray already has implementation for binary operators. But this doesn't happen: it compiles using the <valarray> implementation. Why this doesn't throw an error?

Upvotes: 2

Views: 1348

Answers (1)

Azam Bham
Azam Bham

Reputation: 1399

It is ambiguous because the compiler only looks at the function signature to test for ambiguity, not the body of the function. In your example, this is the function signature:

template<typename pointType>
pointType operator-(pointType a, const pointType & b)

Here, the template parameter pointType could be deduced as std::chrono::time_point. However, there is already a binary minus operator declared in the chrono header for std::chrono::time_point (https://en.cppreference.com/w/cpp/chrono/time_point/operator_arith2). This is what is causing the ambiguity error.

To solve this problem, you should first consider whether you need such a generic binary minus operator. The problem you are currently experiencing will not be unique to std::chrono::time_point, but will also occur with any other header that contains a class with a member or non-member binary minus operator, where both arguments are of the same type (or could implicitly convert into the same type). Perhaps a simple set of function overloads for the types in question:

template<typename T>
std::vector<T> operator-(const std::vector<T>& a, const std::vector<T>& b);

template<typename T, size_t N>
std::array<T,N> operator-(const std::array<T,N>& a, const std::array<T,N>& b);

This would be the safest option. You could also not use operator overloading altogether, and stick to a conventional function:

template<typename T>
T pointwise_subtract(const T& a, const T& b);

If you have a c++20 compiler, you could use concepts. If you insist on using non-member operator templates, you may have to use SFINAE-based template metaprogramming, a more advanced and less readable technique:

//enable this template if the type T has a member method "size" and 
//    subscript operator accepting variables of type "size_t"
template<typename T, typename=std::void_t<
                            decltype(std::declval<T>().size()),
                            decltype(std::declval<T>()[std::declval<size_t>()])
                                         >
T operator-(const T& a, const T& b);

This will remove your ambiguity error.

Upvotes: 1

Related Questions