Search code examples
c++operator-overloadingambiguoustemplate-argument-deduction

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?


Solution

  • 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.