初始化列表-构造不可复制(但可移动)对象的向量
人们可以将不可复制但可移动类型的右值push_back
放入该类型的向量中:
#include <vector>
struct S
{
S(int);
S(S&&);
};
int main()
{
std::vector<S> v;
v.push_back(S(1));
v.push_back(S(2));
v.push_back(S(3));
}
但是,当我尝试使用相同的右值初始化列表构造向量时,我收到有关副本的错误需要构造函数:
#include <vector>
struct S
{
S(int);
S(S&&);
};
int main()
{
std::vector<S> v = {S(1), S(2), S(3)};
}
我在 GCC 4.7 中收到以下错误:
In file included from include/c++/4.7.0/vector:63:0,
from test.cpp:1:
include/c++/4.7.0/bits/stl_construct.h: In instantiation of 'void std::_Construct(_T1*, _Args&& ...) [with _T1 = S, _Args = {const S&}]':
include/c++/4.7.0/bits/stl_uninitialized.h:77:3: required from 'static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const S*, _ForwardIterator = S*, bool _TrivialValueTypes = false]'
include/c++/4.7.0/bits/stl_uninitialized.h:119:41: required from '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const S*, _ForwardIterator = S*]'
include/c++/4.7.0/bits/stl_uninitialized.h:260:63: required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = const S*, _ForwardIterator = S*, _Tp = S]'
include/c++/4.7.0/bits/stl_vector.h:1185:4: required from 'void std::vector<_Tp, _Alloc>::_M_range_initialize(_ForwardIterator, _ForwardIterator, std::forward_iterator_tag) [with _ForwardIterator = const S*, _Tp = S, _Alloc = std::allocator<S>]'
include/c++/4.7.0/bits/stl_vector.h:362:2: required from 'std::vector<_Tp, _Alloc>::vector(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = S, _Alloc = std::allocator<S>, std::vector<_Tp, _Alloc>::allocator_type = std::allocator<S>]'
test.cpp:11:41: required from here
include/c++/4.7.0/bits/stl_construct.h:77:7: error: no matching function for call to 'S::S(const S&)'
include/c++/4.7.0/bits/stl_construct.h:77:7: note: candidates are:
test.cpp:6:5: note: S::S(S&&)
test.cpp:6:5: note: no known conversion for argument 1 from 'const S' to 'S&&'
test.cpp:5:5: note: S::S(int)
test.cpp:5:5: note: no known conversion for argument 1 from 'const S' to 'int'
应该允许这样做吗?我认为没有技术障碍被允许,但我目前没有方便的标准......
One can push_back
rvalues of a noncopyable-but-movable type into a vector of that type:
#include <vector>
struct S
{
S(int);
S(S&&);
};
int main()
{
std::vector<S> v;
v.push_back(S(1));
v.push_back(S(2));
v.push_back(S(3));
}
However, when I try to initializer-list-construct the vector with the same rvalues, I get errors about a copy constructor being required:
#include <vector>
struct S
{
S(int);
S(S&&);
};
int main()
{
std::vector<S> v = {S(1), S(2), S(3)};
}
I get the following errors with GCC 4.7:
In file included from include/c++/4.7.0/vector:63:0,
from test.cpp:1:
include/c++/4.7.0/bits/stl_construct.h: In instantiation of 'void std::_Construct(_T1*, _Args&& ...) [with _T1 = S, _Args = {const S&}]':
include/c++/4.7.0/bits/stl_uninitialized.h:77:3: required from 'static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const S*, _ForwardIterator = S*, bool _TrivialValueTypes = false]'
include/c++/4.7.0/bits/stl_uninitialized.h:119:41: required from '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const S*, _ForwardIterator = S*]'
include/c++/4.7.0/bits/stl_uninitialized.h:260:63: required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = const S*, _ForwardIterator = S*, _Tp = S]'
include/c++/4.7.0/bits/stl_vector.h:1185:4: required from 'void std::vector<_Tp, _Alloc>::_M_range_initialize(_ForwardIterator, _ForwardIterator, std::forward_iterator_tag) [with _ForwardIterator = const S*, _Tp = S, _Alloc = std::allocator<S>]'
include/c++/4.7.0/bits/stl_vector.h:362:2: required from 'std::vector<_Tp, _Alloc>::vector(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = S, _Alloc = std::allocator<S>, std::vector<_Tp, _Alloc>::allocator_type = std::allocator<S>]'
test.cpp:11:41: required from here
include/c++/4.7.0/bits/stl_construct.h:77:7: error: no matching function for call to 'S::S(const S&)'
include/c++/4.7.0/bits/stl_construct.h:77:7: note: candidates are:
test.cpp:6:5: note: S::S(S&&)
test.cpp:6:5: note: no known conversion for argument 1 from 'const S' to 'S&&'
test.cpp:5:5: note: S::S(int)
test.cpp:5:5: note: no known conversion for argument 1 from 'const S' to 'int'
Should this be allowed? I see no technical obstacles to it being allowed, but I don't have the Standard handy at the moment...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
也许 8.5.4.5 中的这个条款解释了这一点(我的重点):
因此,如果对象是可复制的,则只能从列表进行初始化。
更新:正如 Johannes 指出的那样,复制初始化可以通过复制构造函数和移动构造函数来实现,因此仅此不足以回答问题。然而,这里是 18.9 中描述的
initializer_list
类规范的摘录:请注意,没有非常量的 typedef!
我刚刚尝试创建一个 IL 构造函数,该构造函数将通过 std::make_move_iterator 遍历初始化器列表,但由于
const T &
无法转换为T&& 而失败了;
。所以答案是:你不能从 IL 中移出,因为标准是这么说的。
Maybe this clause from 8.5.4.5 explains it (my emphasis):
So you can only initialize from lists if the objects are copyable.
Update: As Johannes points out, copy-initialization can be realized by both copy and move constructors, so that alone isn't enough to answer the question. Here is, however, an excerpt of the specification of the
initializer_list
class as described in 18.9:Note how there are no non-constant typedefs!
I just tried making an IL constructor which would traverse the initializer list via
std::make_move_iterator
, which failed becauseconst T &
cannot be converted toT&&
.So the answer is: You cannot move from the IL, because the standard says so.
initializer_list仅提供const引用和const迭代器。向量无法从那里移动。
The initializer_list only provides const references and const iterators. There is no way for the vector to move from that.
看来这可能是编译器问题。 这适用于 g++ 4.5.1(点击查看 IdeOne 在线演示)
结论:从某种意义上说,旧的 g++ 实现没有正确标记错误;初始值设定项列表不支持移动其元素(元素在过程中隐式复制)。感谢 Kerrek SB 引用有用的标准中的短语。
旧程序(为了理解评论:)
编辑发现至少 g++ 4.6.1+ 似乎有您对此代码的抱怨。
编辑在阅读
std:: 的源代码后initializer_list
我开始觉得库不支持这一点(看起来是故意的)。标准是否实际上允许初始化列表转发其元素的xvalue-ness...如果他们停在那里我不会感到惊讶(完美转发仍然是我认为在 C++0x 中不容易支持,并且并非所有初始化参数都需要具有相同的(可扣除的)类型。任何有更多标准的人都可以帮忙吗? href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2640.pdf" rel="nofollow noreferrer">http://www.open-std.org/ jtc1/sc22/wg21/docs/papers/2008/n2640.pdf
It appears it might be a compiler issue. This works in g++ 4.5.1 (click for IdeOne online demo)
Conclusion: It was, in the sense that older g++ implementations did not correctly flag an error; initializer lists do not support moving their elements (elements are implicitely copied in the process). Thank to Kerrek SB for quoting the helpful phrase from the standard.
Old proceedings (for the sake of understanding the comments:)
Edit Found out that at least g++ 4.6.1+ seem to have your complaint about this code.
Edit Upon reading the source to
std::initializer_list<T>
I'm starting to get the impression that this is not supported by the library (it looks intentional). Whether the standard actually allows for an initializer list to forward the xvalue-ness of it's elements... I wouldn't be surprised if they stopped there (perfect forwarding is still not easily supported in C++0x I think, and not all initializer parameters would need to have the same (deductable) type.Anyone with more standardese under his belt care to help out? http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2640.pdf
Kerrek SB 的回答似乎答案是否定的。但是您可以通过使用可变参数模板的小辅助函数来实现类似的效果:
It seems the answer is No per Kerrek SB's answer. But you can achieve something similar by small helper functions using variadic templates: