23

コピーでき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));
}

ただし、同じ右辺値を使用してベクトルをinitializer-list-constructしようとすると、コピーコンストラクターが必要であるというエラーが発生します。

#include <vector>

struct S
{
    S(int);
    S(S&&);
};

int main()
{
    std::vector<S> v = {S(1), S(2), S(3)};
}

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

これは許可されるべきですか?許可されることに対する技術的な障害は見当たりませんが、現時点ではスタンダードを手元に置いていません...

4

4 に答える 4

14

たぶん、8.5.4.5 のこの節で説明できます (私の強調):

型 std::initializer_list のオブジェクトは、実装が型 E の N 要素の配列を割り当てたかのように、初期化子リストから構築されます。ここで、N は初期化子リスト内の要素の数です。その配列の各要素は、イニシャライザ list の対応する要素でコピー初期化され、その配列を参照するように std::initializer_list オブジェクトが構築されます。

したがって、オブジェクトがコピー可能な場合にのみ、リストから初期化できます。


更新: Johannes が指摘しているように、コピー初期化はコピー コンストラクターとムーブ コンストラクターの両方で実現できるため、それだけでは質問に答えるには不十分です。ただし、initializer_list18.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 がないことに注意してください。

を介して初期化子リストをトラバースする IL コンストラクターを作成しようとしましたが、 に変換できないstd::make_move_iteratorため失敗しました。const T &T&&

したがって、答えは次のとおりです。標準がそう言っているので、ILから移動することはできません。

于 2011-08-29T15:18:47.137 に答える
9

コンパイラの問題である可能性があります。これは g++ 4.5.1 で動作します( IdeOne オンライン デモをクリック)。

結論: 古い g++ 実装ではエラーが正しくフラグ付けされなかったという意味で、そうでした。初期化子リストは要素の移動をサポートしていません (要素はプロセスで暗黙的にコピーされます)。標準から役立つフレーズを引用してくれた Kerrek SB に感謝します。


古い議事録(コメントを理解するため:)

編集少なくとも g++ 4.6.1+ では、このコードについて不満があるようです。

ソースを読むと、std::initializer_list<T>これはライブラリでサポートされていないという印象を受け始めています (意図的に見えます) 標準が実際に初期化子リストがその要素のxvalue-nessを転送することを許可しているかどうか...それらがそこで停止したとしても驚かないでしょう (完全な転送は C++0x ではまだ簡単にサポートされていないと思います。初期化子パラメーターは、同じ (演繹可能) 型である必要があります。

助けてくれる彼のベルトケアの下でより標準的な人はいますか?http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2640.pdf

#include <vector>

struct S
{
    S(int) {};
    S(S&&) {};
};

int main()
{
    std::vector<S> v = {S(1), S(2), S(3)};
    std::vector<S> w = {std::move(S(1)), std::move(S(2)), std::move(S(3))};

    std::vector<S> or_even_just = {1, 2, 3};
}

于 2011-08-29T14:21:09.177 に答える
8

initializer_listは、const参照とconstイテレーターのみを提供します。ベクトルがそこから移動する方法はありません。

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;
于 2011-08-29T15:12:56.417 に答える
5

Kerrek SB's answerによると、答えは No のようです。しかし、可変個引数テンプレートを使用して、小さなヘルパー関数で同様のことを実現できます。

#include <vector>
#include <utility>

template <typename T>
void add_to_vector(std::vector<T>* vec) {}

template <typename T, typename... Args>
void add_to_vector(std::vector<T>* vec, T&& car, Args&&... cdr) {
  vec->push_back(std::forward<T>(car));
  add_to_vector(vec, std::forward<Args>(cdr)...);
}

template <typename T, typename... Args>
std::vector<T> make_vector(Args&&... args) {
  std::vector<T> result;
  add_to_vector(&result, std::forward<Args>(args)...);
  return result;
}

struct S {
  S(int) {}
  S(S&&) {}
};

int main() {
  std::vector<S> v = make_vector<S>(S(1), S(2), S(3));
  return 0;
}
于 2017-03-26T04:19:58.323 に答える