【发布时间】:2023-03-13 08:51:01
【问题描述】:
我试图了解以下代码在 ADL 期间出现的模棱两可的转换警告:
#include <boost/operators.hpp>
#include <boost/polygon/polygon.hpp>
class Scalar
: private boost::multiplicative< Scalar, double > {
public:
explicit Scalar( double val ) : mVal( val ) {}
Scalar &operator*=(double rhs) noexcept {
mVal *= rhs;
return (*this);
}
Scalar &operator/=(double rhs) noexcept {
mVal /= rhs;
return (*this);
}
private:
double mVal;
};
using Coordinate = int;
using Polygon = boost::polygon::polygon_with_holes_data<Coordinate>;
using Point = boost::polygon::polygon_traits<Polygon>::point_type;
template <class T, typename = std::enable_if_t<std::is_arithmetic_v<std::remove_reference_t<T>>>>
Point operator*(const Point &a, T b) noexcept {
return Point(a.x() * b, a.y() * b);
}
int main(int argc, char *argv[]){
Scalar a( 10 );
int b = 10;
Scalar a_times_b = a * b;
return 0;
}
我收到以下 GCC 11.2 警告:
<source>: In function 'int main(int, char**)':
<source>:33:28: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
33 | Scalar a_times_b = a * b;
| ^
In file included from <source>:1:
/opt/compiler-explorer/libs/boost_1_78_0/boost/operators.hpp:268:1: note: candidate 1: 'Scalar boost::operators_impl::operator*(const Scalar&, const double&)'
268 | BOOST_BINARY_OPERATOR_COMMUTATIVE( multipliable, * )
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:26:7: note: candidate 2: 'Point operator*(const Point&, T) [with T = int; <template-parameter-1-2> = void; Point = boost::polygon::point_data<int>]'
26 | Point operator*(const Point &a, T b) noexcept {
| ^~~~~~~~
<source>:33:12: warning: variable 'a_times_b' set but not used [-Wunused-but-set-variable]
33 | Scalar a_times_b = a * b;
|
见https://godbolt.org/z/qzfvjr86c。解决此问题的一种方法是也继承 boost::multiplicative< Scalar, int > 并可能还为 int 定义运算符 *= 和 /=(这在技术上是不必要的,因为我们得到了从 int 到 double 的隐式转换)。
我的困惑:
对于所谓的“第一”,有一个隐式的内置int->double 转换。对于所谓的“第二”,编译器是否在谈论从 Scalar 类到 Point 的一些转换?我不确定这个转换链是什么样子的,因为我没有定义任何将 Scalar 类转换为 Point 的方法。如果启用,我有什么遗漏吗?这是 Boost 或 GCC 中的某种错误吗?
【问题讨论】:
-
“启用 if 是否缺少某些东西” - 我想:它太开放并导致“同样糟糕”的重载匹配通过隐式转换
-
@sehe 我不确定为什么它“同样糟糕”。例如,如果我去掉 SFINAE 并只定义
operator*(const Point &a, int b),即使无法将Scalar转换为Point,我仍然会收到模棱两可的转换警告。 -
我也不确定,但我我确定编译器会竭尽全力通过写一篇关于它的错误小说来准确地向你解释这一点。除非我绝对必须阅读,否则我实际上并没有阅读这些内容的习惯。相反,我避免过度开放的重载并使用 ADL/ADL 障碍来进行选择性。这意味着在全局命名空间中没有运算符,并且很少在作用域内使用
using(除非我愿意冒险在将来出现新的重载时代码会中断) -
如果我删除继承并将运算符定义为友元函数,我会收到相同的警告。猜猜这是编译器的错......
-
从来都不是*。这是关于
Point可构造性的一些假设:no problem vs using Boost Polygon(*适用某些条件)。同样,您的问题是全局命名空间重载过于开放。