是否允许将 std::vector 的元素插入到同一向量中?
Is inserting an element of a std::vector into the same vector allowed?
考虑 std::vector<T>
的以下 insert
和 emplace
成员函数:
template <class... Args> iterator emplace(const_iterator position, Args&&... args);
iterator insert(const_iterator position, const T& x);
iterator insert(const_iterator position, T&& x);
iterator insert(const_iterator position, size_type n, const T& x);
如果其中一个以对向量本身的元素作为参数的引用被调用怎么办?通常,它们中的每一个都会使对从 position
开始的所有元素的引用无效,这可能包括参数,或者如果发生重新分配,对 all 元素的引用,这肯定包括它,但这是否意味着这样的调用无效或插入(似乎)先发生?
查看一些常见的实现会得出奇怪的结果:
libstdc++ 在移动任何元素之前复制参数,但仅限于 insert
的 const T&
重载中。它包含此评论:
The order of the three operations is dictated by the C++0x
case, where the moves could alter a new element belonging
to the existing vector. This is an issue only for callers
taking the element by const lvalue ref (see 23.1/13).
但 C++11 §23.1 只是对容器库的简要总结,即使我们假设这指的是 §23.2.1(在 C++03 中曾经是 §23.1),§23.2 .1/13只给出了allocator-aware container的定义,貌似跟这个没有关系。我已经浏览了第 23 章,但没有找到任何相关内容。
libc++ 在移动 emplace
中的任何元素之前创建一个临时对象,而在 insert
中它首先移动元素但将参数引用转换为指针并调整它以确保它指向原始元素——但同样,它仅在 const T&
重载中执行所有这些操作。
Visual C++ 在所有情况下都会在移动任何元素之前创建一个 copy/temporary。
我是否错过了标准定义此行为的地方?为什么我查看的三个 C++ 库彼此不一致?为什么 libstdc++ 评论说它只是 insert(const_iterator, const T&)
的问题?如果标准不要求它起作用,为什么图书馆还要费心让它起作用呢? (当然,这会花费一些副本 and/or 移动,否则可以避免。)最后,如果我要实现一个应该类似于 std::vector
的容器,我应该让它工作吗?
首先回答第二个问题:标准明确表示允许标准库假设当通过右值引用传递某些东西时,右值引用是 only 引用那个对象。这意味着它不能合法地成为矢量的元素。 C++11 17.6.4.9/1相关部分:
- If a function argument binds to an rvalue reference parameter, the implementation may assume that
this parameter is a unique reference to this argument. ... [ Note: If a program casts an lvalue to an
xvalue while passing that lvalue to a library function (e.g. by calling the function with the argument
move(x)
), the program is effectively asking that function to treat that lvalue as a temporary. The
implementation is free to optimize away aliasing checks which might be needed if the argument was
an lvalue. —end note ]
这让我们只需要处理 const T &
案例。尽管 libstdc++ 和 libc++ 在这种情况下有所不同,但它们的最终结果是相同的——它们将从传入的对象中正确复制。标准只规定了行为,没有规定实现。只要他们达到正确的行为,他们就没事。
考虑 std::vector<T>
的以下 insert
和 emplace
成员函数:
template <class... Args> iterator emplace(const_iterator position, Args&&... args);
iterator insert(const_iterator position, const T& x);
iterator insert(const_iterator position, T&& x);
iterator insert(const_iterator position, size_type n, const T& x);
如果其中一个以对向量本身的元素作为参数的引用被调用怎么办?通常,它们中的每一个都会使对从 position
开始的所有元素的引用无效,这可能包括参数,或者如果发生重新分配,对 all 元素的引用,这肯定包括它,但这是否意味着这样的调用无效或插入(似乎)先发生?
查看一些常见的实现会得出奇怪的结果:
libstdc++ 在移动任何元素之前复制参数,但仅限于
insert
的const T&
重载中。它包含此评论:The order of the three operations is dictated by the C++0x case, where the moves could alter a new element belonging to the existing vector. This is an issue only for callers taking the element by const lvalue ref (see 23.1/13).
但 C++11 §23.1 只是对容器库的简要总结,即使我们假设这指的是 §23.2.1(在 C++03 中曾经是 §23.1),§23.2 .1/13只给出了allocator-aware container的定义,貌似跟这个没有关系。我已经浏览了第 23 章,但没有找到任何相关内容。
libc++ 在移动
emplace
中的任何元素之前创建一个临时对象,而在insert
中它首先移动元素但将参数引用转换为指针并调整它以确保它指向原始元素——但同样,它仅在const T&
重载中执行所有这些操作。Visual C++ 在所有情况下都会在移动任何元素之前创建一个 copy/temporary。
我是否错过了标准定义此行为的地方?为什么我查看的三个 C++ 库彼此不一致?为什么 libstdc++ 评论说它只是 insert(const_iterator, const T&)
的问题?如果标准不要求它起作用,为什么图书馆还要费心让它起作用呢? (当然,这会花费一些副本 and/or 移动,否则可以避免。)最后,如果我要实现一个应该类似于 std::vector
的容器,我应该让它工作吗?
首先回答第二个问题:标准明确表示允许标准库假设当通过右值引用传递某些东西时,右值引用是 only 引用那个对象。这意味着它不能合法地成为矢量的元素。 C++11 17.6.4.9/1相关部分:
- If a function argument binds to an rvalue reference parameter, the implementation may assume that this parameter is a unique reference to this argument. ... [ Note: If a program casts an lvalue to an xvalue while passing that lvalue to a library function (e.g. by calling the function with the argument
move(x)
), the program is effectively asking that function to treat that lvalue as a temporary. The implementation is free to optimize away aliasing checks which might be needed if the argument was an lvalue. —end note ]
这让我们只需要处理 const T &
案例。尽管 libstdc++ 和 libc++ 在这种情况下有所不同,但它们的最终结果是相同的——它们将从传入的对象中正确复制。标准只规定了行为,没有规定实现。只要他们达到正确的行为,他们就没事。