簡體   English   中英

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定義運算符 *= 和 /= (這在技術上是不必要的,因為我們得到了從intdouble的隱式轉換)。

我的困惑:

對於所謂的“第一”,有一個隱式的內置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 (未顯示)

現場演示

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM