移动容器的迭代器?

alf*_*lfC 9 iterator stl move const-iterator c++11

C++ 98容器定义了两种迭代器,::iterators和::const_iterators.一般来说,像这样:

struct vec{
   iterator begin();
   const_iterator begin() const;
};
Run Code Online (Sandbox Code Playgroud)

在C++ 11中,这部分设计似乎没有改变.问题是, 为了保持一致性和实际目的,添加::move_iterators也是有意义的吗?或者它是一种矫枉过正.

我可以想象一个右值容器可能会移动它们的元素.

class vec{
   iterator begin() &;
   const_iterator begin() const&;
   move_iterator begin() &&;
};
Run Code Online (Sandbox Code Playgroud)

如果我理解正确,可以在简单的情况下实现:

auto vec::begin() &&{return std::make_move_iterator(this->begin());}
Run Code Online (Sandbox Code Playgroud)

当然,普通的迭代器可以转换为移动迭代器(带std::make_move_iterator),但动机是通用代码.

例如,使用移动迭代器,这将非常优雅地实现,没有条件,具体取决于参数是左值还是右值.

template<class Container, class T = Container::value_type>
void transport_first(Container&& c, std::vector<T>& v){
    v.emplace_back(*std::forward<Container>(c).begin());
}
Run Code Online (Sandbox Code Playgroud)

请注意,如果可能,此代码不会产生任何不必要的副本.如何在没有move_iterators生成的情况下实现begin.


我也意识到这个问题几乎适用于容器的任何访问器,例如operator[],front()back().

template<class Value>
class vec{
   using value_type = Value;
   using reference = Value&;
   using const_reference = Value const&;
   using rvalue_reference = Value&&; // NEW!
   reference front() &{...}
   rvalue_reference front() &&{...} // NEW!
   const_reference front() const&{...}
};
Run Code Online (Sandbox Code Playgroud)

也许容器应该在C++ 11中从头开始重新设计.他们的设计显示了它的时代.


有一个建议,自动推导出(decl)类型,(*this)基本上具有所有相应的begin(和其他成员函数)的重载是免费的.

https://youtu.be/yB4E-SzQPdI?t=4131

眠りネ*_*ネロク 5

STL容器旨在与STL算法结合使用.目前在STL中找到的通用算法处理迭代器,但是你的模板函数transport_first:

template<class Container, class T = Container::value_type>
void transport_first(Container&& c, std::vector<T>& v){
    v.emplace_back(*std::forward<Container>(c).begin());
}
Run Code Online (Sandbox Code Playgroud)

由基于容器的代码组成,即:它在容器而不是迭代器上运行.我们也许能够找到这样的基于容器的算法的STL为C++ 20个感谢的部分概念.

通用transport_first算法的"类STL方法" 实际上是:

template<typename InIt, typename OutIt>
void transport_first(InIt src, OutIt dst) {
    *dst = *src;
}
Run Code Online (Sandbox Code Playgroud)

遵循这种方法(即:迭代器而不是容器),当涉及到通用代码时,是否使用是否move_iterator可以简单地在"最顶层"通用算法中确定,因为传递的迭代器被进一步复制(即:通过值传递)进入"潜在的"算法).

另外,与transport_first上面基于迭代器的不同,STL充满了基于迭代器对的算法(例如:) std::copy来实现范围.这个接口使得在rvalues上重载那些容器成员函数没什么吸引力,因为正如在另一个答案中已经说明的那样,那些重载的成员函数将被调用,尤其是在未命名的(非const)容器对象上,并且未命名的容器对象被限制为在单个表达式中使用,通过在同一容器对象上调用两个和成员函数来阻止迭代器对的创建.begin()end()


对容器真正有意义的是,有新的新成员函数来返回相应的move_iterator对象:

move_iterator Container<T>::mbegin();
move_iterator Container<T>::mend();
Run Code Online (Sandbox Code Playgroud)

这只是使用std::make_move_iterator返回的成员函数返回的值调用的快捷方式iterator.成员函数mbegin()将在基于迭代器的函数中使用transport_first:

transport_first(coll.mbegin(), std::back_inserter(vec));
Run Code Online (Sandbox Code Playgroud)

相应的函数模板std::mbegin()std::mend()也将使意义:

transport_first(std::mbegin(coll), std::back_inserter(vec));
Run Code Online (Sandbox Code Playgroud)


Seb*_*edl 4

您可以轻松地将任何非常量迭代器转换为移动迭代器。它通常对容器没有影响。

您不能轻易地将非常量迭代器转换为常量迭代器。例如,当采用非常量迭代器(使用 )时,写时复制字符串(std::string过去对于某些编译器来说,自定义字符串仍然可能是这样)必须悲观地从共享数据中分离,begin()以便满足典型的失效保证,当你只想要一个常量迭代器时,这是非常低效的。

此外,C++ 重载规则不允许您在begin()不将未指定版本更改为左值的情况下引入右值重载,这将是一个重大更改。

最后,重载begin()右值无论如何都没有用 - 期望是在右值上调用右值函数,并且除了 产生的右值之外std::move,这些右值 1) 很快就会消失(这将使获得的迭代器无效)并且 2) 没有名称,这意味着它们只能在一个表达式中使用,这意味着您不能同时调用begin()end()来获取迭代器对,并且单个迭代器是无用的,因为您永远无法知道取消引用它是否安全。