编译器抛出 "ambiguous overload for operator"

Compiler throws "ambiguous overload for operator"

我正在学习如何使用 std::chrono,我想制作一个易于使用的模板 class 计时器(在 timer.h 中定义)。测试程序成功,一切正常,直到我尝试在一个程序中使用我的新 Timer,其中定义了一些模板运算符,这与 Timer 内部使用的运算符冲突。

Inside Timer 我必须在 std::chrono::time_point 类型的两个变量(start_timeend_time)之间使用 operator-,以获得 duration包含经过时间的变量。

在另一个 header (algebra.h) 中,我实现了二进制 operator- 的重载以区分两个 std::vector 或两个 std::array,或者还有一个 user-defined 容器,带有 operator[]size() 成员函数。

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;
}

当我尝试同时包含 timer.halgebra.h 时,编译器会抛出一个错误,指出 "ambiguous overload for operator-" 建议,作为可能的候选对象,algebra.halgebra.h 中的运算符和在 <chrono>.

中实施的那个

我不明白为什么它是模棱两可的,因为pointType不能推导为std::chrono::time_point,因为它没有operator[]size()成员功能。

P.S。我尝试了其他方法来解决这个问题,但我只是在测试使用 std::valarray 的程序时变得更加困惑。当我同时包含 <valarray>"algebra.h" 并尝试区分两个 valarray 时,我预计编译器会抱怨 operator- 的定义不明确,因为 std::valarray 已经有了二元运算符的实现。但这并没有发生:它使用 <valarray> 实现进行编译。为什么这不会引发错误?

它是有歧义的,因为编译器只查看函数签名来测试歧义,而不是函数的body。在您的示例中,这是函数签名:

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

这里模板参数pointType可以推导为std::chrono::time_point。但是,在 chrono header 中已经为 std::chrono::time_point (https://en.cppreference.com/w/cpp/chrono/time_point/operator_arith2) 声明了二元减号运算符。这就是导致歧义错误的原因。

要解决这个问题,首先要考虑是否需要这样一个通用的二元减运算符。您当前遇到的问题不会是 std::chrono::time_point 所独有的,但也会出现在任何其他 header 中,其中包含带有成员的 class 或 non-member 二元减号运算符,其中两个参数属于同一类型(或者可以隐式转换为同一类型)。也许有问题的类型的一组简单的函数重载:

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);

这是最安全的选择。您也可以完全不使用运算符重载,而坚持使用常规函数:

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

如果你有一个 c++20 编译器,你可以使用概念。如果你坚持使用 non-member 运算符模板,你可能不得不使用 SFINAE-based 模板元编程,一种更高级但可读性较差的技术:

//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);

这将消除您的歧义错误。