【问题标题】:Compiler throws "ambiguous overload for operator"编译器抛出“运算符的模棱两可的重载”
【发布时间】:2020-04-30 22:36:52
【问题描述】:

我正在学习如何使用 std::chrono 并且我想让模板类 Timer 易于使用(在 timer.h 中定义)。测试程序很成功,一切正常,直到我尝试在一个定义了一些模板运算符的程序中使用我的新 Timer,这与 Timer 中使用的运算符冲突。

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

在另一个标头 (algebra.h) 中,我实现了二进制 operator- 的重载,以区分两个 std::vector 或两个 std::array,或者还提供了一个用户定义的容器 operator[] 和 @ 987654332@成员函数。

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 时,编译器会抛出一个错误,提示“运算符重载不明确”,建议可能的候选者是 algebra.h 中的运算符和 @987654337 中实现的运算符@。

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

附:我尝试了其他方法来解决它,但我只是在测试使用std::valarray 的程序时更加困惑。当我同时包含&lt;valarray&gt;"algebra.h" 并尝试在两个valarray 之间做出区别时,我希望编译器会抱怨operator- 的模糊定义,因为std::valarray 已经实现了二元运算符。但这不会发生:它使用&lt;valarray&gt; 实现进行编译。为什么这不会引发错误?

【问题讨论】:

  • 简而言之,对于没有您编写的类的操作数的情况,您不应该添加运算符重载。操作员的名称查找可能找不到它。

标签: c++ operator-overloading ambiguous template-argument-deduction


【解决方案1】:

这是模棱两可的,因为编译器只查看函数签名来测试模棱两可,而不是函数体。在您的示例中,这是函数签名:

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

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

要解决这个问题,首先要考虑是否需要这样一个通用的二元减号运算符。您当前遇到的问题不会是 std::chrono::time_point 独有的,但也会出现在包含具有成员或非成员二进制减号运算符的类的任何其他标头中,其中两个参数属于相同类型(或可能隐含转换成相同的类型)。可能是针对相关类型的一组简单的函数重载:

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 编译器,你可以使用概念。如果您坚持使用非成员运算符模板,则可能必须使用基于 SFINAE 的模板元编程,这是一种更高级且可读性较差的技术:

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

这将消除您的歧义错误。

【讨论】:

  • 我不知道歧义测试仅针对函数签名!谢谢!但此时我不明白为什么编译器不抱怨 valarray 运算符的歧义。
  • @dariobaron 这是因为已经为 valarray 定义的运算符比您在上面定义的通用运算符更专业。它们的签名如下:template std::valarray operator-(const std::valarray&, const std::valarray&)。注意它们的参数是 std::valarray 的特定类型。在这种情况下,在重载决议期间,编译器将更喜欢更专业的模板而不是更通用的模板。
  • 非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-18
  • 2010-12-10
  • 1970-01-01
  • 2014-09-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多