std::accumulate 中的无效转换

invalid convertion in std::accumulate

我有一个 class 表示 N 维度中的一个点,具有 min 静态函数(逐个字段的最小值)

template<typename T, std::size_t N>
class Point : public std::array<T,N>
{
public:
    template<typename... Args>
    Point(Args&&... args) : std::array<T,N>{{args...}} {}

    // ...

    static Point min(const Point&, const Point&) {
        // ...  
    }
};

我写的时候一切正常

Point<float,3> a = {0.f, 1.f, 2.f};
Point<float,3> b = {2.f, 1.f, 0.f};
Point<float,3> c = Point<float,3>::min(a,b); // OK

但是如果我尝试在数组上使用 std::accumulate

Point<float,3> array[100] = ... ;
Point<float,3> min = std::accumulate(array, array+100, array[0], Point<float,3>::min); // Error

我收到一个错误:

error: cannot convert ‘Point<float, 3ul>’ to ‘float’ in initialization
adimx::Point<T,N>::Point(Args&&... args) : std::array<T,N>{{args...}}

这是 std::accumulate 实现与我的构造函数不兼容的问题吗?

由于构造函数通过重载决议获胜,您可以提供所需构造函数的默认版本:

Point(Point&) = default;
Point(const Point&) = default;
Point(Point&&) = default;
Point& operator=(const Point&) = default;

这是一种约束该构造函数的方法,以便它仅在所有参数都可以隐式转换为 float:

时才参与重载决议
template<bool... > class bool_pack;
template<bool... b>
using all_true = std::is_same<bool_pack<true, b...>, bool_pack<b..., true>>;

template<typename... Args,
         class = typename std::enable_if<all_true<std::is_convertible<Args, float>::value...>::value>::type>
Point(Args&&... args) : std::array<T,N>{{args...}} {}

新解决方案:

template <bool J>
using disable_if=enable_if<!J>;

template <typename T,typename... Tail> struct getHead
{
    typedef typename decay<T>::type type;
};

template<typename... Args,typename = typename disable_if<is_same<typename getHead<Args...>::type,Point<T,N> >::value >::type >
    Point(Args&&... args) : std::array<T,N> {{args...}}
    {
        //...
    }

我相信这个解决方案是 perfect.We 仅当参数为 Point 时停止转发引用变量参数构造函数 itself.Any 其他类型仍然调用转发引用变量参数构造函数。 不管Point<float,3>还是Point<int,3>还是Point<int,2>还是Point<user-define,numx>都OK。


至少有三种解决方案供您选择。

首先,为了避免转发引用变量参数构造函数劫持复制构造函数,所以去掉这个函数,而不是

template<typename... Args>
        Point(Args... args) : std::array<T,N> {{args...}} {}//remove &&

这个解决方案是为了避免问题而不是解决问题。

其次,正如TC所说,停止转发引用变量参数构造函数劫持的构造函数,只要当所有类型都适合时启用。这种方式比较复杂,也削弱了你模板的适用范围。

第三,如MSalters所说,将array[0]改为Point<float,3>(0,0,0)

Point<float,3> min = std::accumulate(array, array+100, Point<float,3>(0,0,0), Point<float,3>::min);

可以,但是为什么?

作为 n4260 12.9 31.3 C++ Standrad 说:

when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move

所以accumulate直接调用Point三个float的构造函数,不调用拷贝构造函数所以不调用转发引用变量参数的构造函数传递一个Point Object作为参数,这是你编译错误的地方。

缺点是每次使用accumulate函数都需要传入右值,不能是左值。