移动容器的迭代器?

时间:2018-02-21 21:37:36

标签: c++11 stl iterator move const-iterator

C ++ 98容器定义了两种迭代器::iterator::const_iterators。通常,像这样:

struct vec{
   iterator begin();
   const_iterator begin() const;
};

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

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

class vec{
   iterator begin() &;
   const_iterator begin() const&;
   move_iterator begin() &&;
};

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

auto vec::begin() &&{return std::make_move_iterator(this->begin());}

当然,普通的迭代器可以转换为移动迭代器(带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());
}

请注意,如果可能,此代码不会产生任何不必要的副本。 如果没有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&{...}
};

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

有一个提议,自动推断(*this)的{​​decl}类型,基本上具有免费的所有相应的begin(和其他成员函数)重载。

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

2 个答案:

答案 0 :(得分: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());
}

基于容器的代码组成,即:它在容器上运行,而不是在迭代器上运行。由于概念,我们或许可以在C ++ 20中找到这种基于容器的算法作为STL的一部分。

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

template<typename InIt, typename OutIt>
void transport_first(InIt src, OutIt dst) {
    *dst = *src;
}

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

此外,与上面基于迭代器的transport_first不同,STL充满了基于迭代器对的算法(例如:std::copy)来实现范围。这个接口使得 rvalues 上的容器成员函数重载变得很有吸引力,因为正如this other answer中所述,那些重载的成员函数将被调用,其中包括未命名的(非 - {{1容器对象和未命名的容器对象仅限于在单个表达式中使用,通过调用constbegin()成员函数来阻碍迭代器对的创建在同一个容器对象上。

对容器真正有意义的是使用新的新成员函数来返回相应的end()对象:

move_iterator

这只是使用返回move_iterator Container<T>::mbegin(); move_iterator Container<T>::mend(); 的成员函数返回的值调用std::make_move_iterator的快捷方式。成员函数iterator将在基于迭代器的mbegin()中用作:

transport_first

相应的功能模板transport_first(coll.mbegin(), std::back_inserter(vec)); std::mbegin()也有意义:

std::mend()

答案 1 :(得分:4)

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

您不能将非const迭代器简单地转换为const迭代器。例如,对于某些编译器来说,一个写入时复制字符串(std::string,自定义字符串仍可能是这个)在采用非const迭代器时必须与共享数据分离(使用{{1为了实现典型的失效保证,当你只想要一个const迭代器时这是非常低效的。

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

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