Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
574 views
in Technique[技术] by (71.8m points)

c++ - Initializer-list-constructing a vector of noncopyable (but movable) objects

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...

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Maybe this clause from 8.5.4.5 explains it (my emphasis):

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.


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;

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 because const T & cannot be converted to T&&.

So the answer is: You cannot move from the IL, because the standard says so.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...