编译器抛出 "ambiguous overload for operator"
Compiler throws "ambiguous overload for operator"
我正在学习如何使用 std::chrono,我想制作一个易于使用的模板 class 计时器(在 timer.h
中定义)。测试程序成功,一切正常,直到我尝试在一个程序中使用我的新 Timer,其中定义了一些模板运算符,这与 Timer 内部使用的运算符冲突。
Inside Timer 我必须在 std::chrono::time_point
类型的两个变量(start_time
和 end_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.h
和 algebra.h
时,编译器会抛出一个错误,指出 "ambiguous overload for operator-" 建议,作为可能的候选对象,algebra.h
和 algebra.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);
这将消除您的歧义错误。
我正在学习如何使用 std::chrono,我想制作一个易于使用的模板 class 计时器(在 timer.h
中定义)。测试程序成功,一切正常,直到我尝试在一个程序中使用我的新 Timer,其中定义了一些模板运算符,这与 Timer 内部使用的运算符冲突。
Inside Timer 我必须在 std::chrono::time_point
类型的两个变量(start_time
和 end_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.h
和 algebra.h
时,编译器会抛出一个错误,指出 "ambiguous overload for operator-" 建议,作为可能的候选对象,algebra.h
和 algebra.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);
这将消除您的歧义错误。