[英]ADL warning: ambiguous conversion with boost operators and SFINAE
I'm trying to understand an ambiguous conversion warning during ADL for the following piece of code:我试图了解以下代码在 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;
}
I get the following warning for GCC 11.2:对于 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;
|
See https://godbolt.org/z/qzfvjr86c .请参阅https://godbolt.org/z/qzfvjr86c 。 One way to fix this is to also inherit from boost::multiplicative< Scalar, int >
and perhaps also define the operators *= and /= for int
(which is technically unnecessary since we get implicit conversions from int
to double
).解决此问题的一种方法是也继承boost::multiplicative< Scalar, int >
并且可能还为int
定义运算符 *= 和 /= (这在技术上是不必要的,因为我们得到了从int
到double
的隐式转换)。
My Confusion:我的困惑:
For the so called "first" there is a implicit built in int->double
conversion.对于所谓的“第一”,有一个隐式的内置int->double
转换。 For the so called "second" is the compiler talking about some conversion from the Scalar class to Point?对于所谓的“第二”,编译器是否在谈论从标量 class 到点的一些转换? I'm not sure what this conversion chain looks like as I haven't defined any way for the Scalar class to be converted to a Point.我不确定这个转换链是什么样子,因为我没有定义任何将标量 class 转换为点的方法。 Is there something I'm missing with the enable if?如果启用,我有什么遗漏吗? Is this some sort of bug in Boost or GCC?这是 Boost 或 GCC 中的某种错误吗?
Your free operator*
in the global namespace is too open.您在全局命名空间中的免费operator*
太开放了。 It actively assumes that Point
is not constructible from Scalar
, quod non:它积极假设Point
不能从Scalar
构造,例如:
You should restrict it more:你应该更多地限制它:
template <
typename P, typename S,
typename IsPoint = typename boost::polygon::is_point_concept<
typename boost::polygon::geometry_concept<P>::type>::type,
typename = std::enable_if_t<IsPoint::value and std::is_arithmetic_v<S>>>
auto operator*(P const& a, S const& b) noexcept {
return boost::polygon::construct<Point>(a.x() * b, a.y() * b);
}
I also suggest you我也建议你
boost::polygon::scale
that already exists and likely has more safeties and/or optimization (not shown)使用已经存在并且可能具有更多安全性和/或优化的boost::polygon::scale
(未显示)Live On Compiler Explorer Live On 编译器资源管理器
#include <type_traits>
struct Scalar {
explicit Scalar(double val) : mVal(val) {}
private:
friend Scalar operator*(Scalar lhs, double rhs) noexcept {
lhs.mVal *= rhs;
return lhs;
}
double mVal;
};
#include <boost/polygon/polygon.hpp>
using Coordinate = int;
using Polygon = boost::polygon::polygon_with_holes_data<Coordinate>;
using Point = boost::polygon::polygon_traits<Polygon>::point_type;
template <
typename P, typename S,
typename IsPoint = typename boost::polygon::is_point_concept<
typename boost::polygon::geometry_concept<P>::type>::type,
typename = std::enable_if_t<IsPoint::value and std::is_arithmetic_v<S>>>
auto operator*(P const& a, S const& b) noexcept {
return boost::polygon::construct<Point>(a.x() * b, a.y() * b);
}
static_assert(std::is_arithmetic_v<double>);
static_assert(not std::is_arithmetic_v<Scalar>);
int main()
{
auto s = Scalar(10) * 10;
auto p = Point(10, 20) * 42;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.