初始化列表构造不可复制(但可移动)对象的向量
可以将不可复制但可移动类型的 push_back
右值转化为该类型的向量:
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)};
}
我在使用 GCC 4.7 时遇到以下错误:
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...
推荐答案
也许 8.5.4.5 的这个条款解释了它(我的重点):
Maybe this clause from 8.5.4.5 explains it (my emphasis):
一个 std::initializer_list 类型的对象是从一个初始化列表就好像实现分配了一个 N 的数组E 类型的元素,其中 N 是初始化列表.该数组的每个元素都被复制初始化与初始化列表的相应元素,以及构造 std::initializer_list 对象以引用该数组.
An object of type std::initializer_list is constructed from an initializer list as if the implementation allocated an array of N elements of type E, where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list object is constructed to refer to that array.
所以如果对象是可复制的,你只能从列表初始化.
So you can only initialize from lists if the objects are copyable.
更新:正如 Johannes 所指出的,复制初始化可以通过复制和移动构造函数来实现,因此仅凭这一点还不足以回答这个问题.然而,这里是 18.9 中描述的 initializer_list
类规范的摘录:
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:
template<class _E>
class initializer_list
{
public:
typedef _E value_type;
typedef const _E& reference;
typedef const _E& const_reference;
typedef size_t size_type;
typedef const _E* iterator;
typedef const _E* const_iterator;
注意没有非常量的 typedef!
Note how there are no non-constant typedefs!
我只是尝试制作一个 IL 构造函数,它会通过 std::make_move_iterator
遍历初始化列表,但失败了,因为 const T &
无法转换为 T&&
.
I just tried making an IL constructor which would traverse the initializer list via std::make_move_iterator
, which failed because const T &
cannot be converted to T&&
.
所以答案是:你不能离开 IL,因为标准是这样说的.
So the answer is: You cannot move from the IL, because the standard says so.
相关文章