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(和其他成员函数)重载。
答案 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容器对象和未命名的容器对象仅限于在单个表达式中使用,通过调用const
和begin()
成员函数来阻碍迭代器对的创建在同一个容器对象上。
对容器真正有意义的是使用新的新成员函数来返回相应的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::move
和begin()
来获得迭代器对,并且单个迭代器是无用的,因为你永远无法知道它是否可以安全地取消引用它。