检查范围的元素是否可以移动?

check if elements of a range can be moved?

本文关键字:移动 是否 范围 元素 检查      更新时间:2023-10-16

我正在尝试 c++20 的范围接口,我添加了一个构造函数,该构造函数在我的类似容器的类型中采用范围。

class element {
...
};
class myclass {
public:
template <typename Iter>
myclass(Iter first, Iter last)
: els(first, last)
{ }
template <typename Range>
myclass(Range&& r);
private:
std::vector<element> els;
};

迭代器对版本非常简单。在els_(first, last);中,如果Iter是普通迭代器,它就会复制元素,如果Iter是可移动迭代器,例如std::move_iterator<>,它会移动元素。如果调用方希望移动元素,则调用方有责任显式提供可移动迭代器。

但是,在范围版本中,尽管我可以检查范围本身是否在右值引用或左值引用中给出,但它无助于检查元素是否可以移动。

假设我们有一个范围生成器make_range(),它采用一个容器并返回一个符合范围概念的代理实例。在下面的代码中,构造函数Range在这两种情况下都是右值引用,但显然在第二种情况下不应移动元素。

std::list<element> list_of_elements{...};
myclass c(std::move(list_of_elements)); // should be moved
std::list<element> list_of_elements_to_be_reused{...};
myclass c(make_range(list_of_elements_to_be_reused)); // should not be moved

如何检查给定的范围是否用于复制以进行移动?

你没有。你信任std::ranges::begin返回的迭代器,就像你信任迭代器Iter做正确的事情一样。第二个构造函数只能委派:

template <std::ranges::Range Range> // Constraints checked with the library concept
myclass(Range&& r) : myclass(std::ranges::begin(std::move(r)), std::ranges::end(std::move(r)))
{}

默认行为复制,这是明智的。但由于std::ranges::begin是一个自定义点对象,因此它可以通过 ADL 拾取用户定义的begin重载。对于用户定义的类型,此设置:

namespace myns {
class myclass { /* ... */ };
auto begin(std::vector<myclass>&& v) { return std::make_move_iterator(v.begin()); }
auto end(std::vector<myclass>&& v) { /* ... * }
}

将在类构造函数传递右值向量时进行std::ranges::begin调用myns::begin。控制行为的是用户定义的类型,这很好。