ADL 警告:使用 boost 运算符和 SFINAE 的转换不明确
ADL warning: ambiguous conversion with boost operators and SFINAE
我正在尝试理解 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
转换。对于所谓的“第二个”,编译器是否在谈论从标量 class 到点的某种转换?我不确定这个转换链是什么样的,因为我还没有定义任何将标量 class 转换为点的方法。如果启用,我是否缺少某些东西?这是 Boost 或 GCC 中的某种错误吗?
您在全局命名空间中的免费 operator*
太开放了。它积极地假设 Point
不能从 Scalar
构造,并且不是:
您应该对其进行更多限制:
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);
}
我也推荐你
- 如图所示,使用点特征来构造点而不是假设直接构造
- 不要将运算符模板放在全局命名空间中(未显示)
- 使用
boost::polygon::scale
已经存在并且可能具有更多安全性 and/or 优化(未显示)
现场演示
#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;
}
我正在尝试理解 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
转换。对于所谓的“第二个”,编译器是否在谈论从标量 class 到点的某种转换?我不确定这个转换链是什么样的,因为我还没有定义任何将标量 class 转换为点的方法。如果启用,我是否缺少某些东西?这是 Boost 或 GCC 中的某种错误吗?
您在全局命名空间中的免费 operator*
太开放了。它积极地假设 Point
不能从 Scalar
构造,并且不是:
您应该对其进行更多限制:
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);
}
我也推荐你
- 如图所示,使用点特征来构造点而不是假设直接构造
- 不要将运算符模板放在全局命名空间中(未显示)
- 使用
boost::polygon::scale
已经存在并且可能具有更多安全性 and/or 优化(未显示)
现场演示
#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;
}