72

誰もが から作成std::vectorしますがstd::initializer_list、その逆はどうでしょうか。

例えば。パラメータとして aを使用する場合std::initializer_list:

void someThing(std::initializer_list<int> items)
{
...
}

vector<T>アイテムがリテラル リストではなく、 a にある場合があります。

std::vector<int> v;
// populate v with values
someThing(v); // boom! No viable conversion etc.

より一般的な質問はstl::initializer_list、 だけでなく、STL iterable からを作成する方法std::vectorです。

4

6 に答える 6

38

答えはノーです。それはできません。

タイプstd::initializer_list<T>のオブジェクトは、タイプ T のオブジェクトの配列へのアクセスを提供する軽量のプロキシ オブジェクトです。std::initializer_listオブジェクトは、次の場合に自動的に構築されます。

  • ブレース初期化リストは、関数呼び出しリストの初期化と代入式を含むリストの初期化で使用されます (コンストラクターの初期化リストと混同しないでください)。
  • 範囲指定された for ループを含め、braced-init-list が auto にバインドされている

ライブラリのサポートに関する限りstd::initializer_list、空のリストを構築するデフォルトのコンストラクターのみがあり、その反復子は定数です。メンバーがないというpush_back()ことは、たとえば、std::copyイテレーターstd::back_inserterアダプターを使用してそれを埋めることができず、そのようなイテレーターを介して直接割り当てることもできないことを意味します。

#include <algorithm>
#include <initializer_list>
#include <iterator>
#include <vector>

int main() 
{
    auto v = std::vector<int> { 1, 2 };
    std::initializer_list<int> i;
    auto it = std::begin(i);
    *it = begin(v); // error: read-only variable is not assignable
}

実際の例

標準コンテナーを見ると、コンストラクター/インサーターで受け入れることに加えてstd::initializer_list、それらはすべてイテレーターのペアを受け取るコンストラクター/インサーターを持っており、実装はinitializer_list関数を対応するイテレーター ペア関数に委譲する可能性があります。たとえば、std::vector<T>::insertlibc++ の関数は次の単純なワンライナーです。

 iterator insert(const_iterator __position, initializer_list<value_type> __il)
        {return insert(__position, __il.begin(), __il.end());}

同様の行に沿ってコードを変更する必要があります。

void someThing(std::initializer_list<int> items)
{
    someThing(items.begin(), items.end()); // delegate
}

template<class It>
void someThing(It first, It last)
{
    for (auto it = first, it != last; ++it) // do your thing
}

リテラルリストではなくベクトルに項目がある場合:

std::vector<int> v = { 1, 2 };
auto i = { 1, 2 };
someThing(begin(v), end(v)); // OK
someThing(i); // also OK
someThing({1, 2}); // even better
于 2013-09-19T14:10:31.807 に答える
2

機能しているように見える方法を投稿しましたが、残念ながら、initializer_lists がローカル スコープの値のコピーへの参照として扱われるため、メモリ アクセス違反が発生しました。

これが代替案です。個別の関数と個別の静的イニシャライザ リストが、可能な項目数ごとに生成され、パラメータ パックでカウントされます。これはスレッド セーフではなく、const_cast (非常に悪いと見なされます) を使用して静的な initializer_list メモリに書き込みます。ただし、gcc と clang の両方で問題なく動作します。

なんらかの理由でこの問題を解決する必要があり、他に選択肢がない場合は、このハックを試すことができます。

#include <initializer_list>
#include <iostream>
#include <stdexcept>
#include <type_traits>
#include <vector>

namespace __range_to_initializer_list {

    constexpr size_t DEFAULT_MAX_LENGTH = 128;

    template <typename V> struct backingValue { static V value; };
    template <typename V> V backingValue<V>::value;

    template <typename V, typename... Vcount> struct backingList { static std::initializer_list<V> list; };
    template <typename V, typename... Vcount>
    std::initializer_list<V> backingList<V, Vcount...>::list = {(Vcount)backingValue<V>::value...};

    template <size_t maxLength, typename It, typename V = typename It::value_type, typename... Vcount>
    static typename std::enable_if< sizeof...(Vcount) >= maxLength,
    std::initializer_list<V> >::type generate_n(It begin, It end, It current)
    {
        throw std::length_error("More than maxLength elements in range.");
    }

    template <size_t maxLength = DEFAULT_MAX_LENGTH, typename It, typename V = typename It::value_type, typename... Vcount>
    static typename std::enable_if< sizeof...(Vcount) < maxLength,
    std::initializer_list<V> >::type generate_n(It begin, It end, It current)
    {
        if (current != end)
            return generate_n<maxLength, It, V, V, Vcount...>(begin, end, ++current);

        current = begin;
        for (auto it = backingList<V,Vcount...>::list.begin();
             it != backingList<V,Vcount...>::list.end();
             ++current, ++it)
            *const_cast<V*>(&*it) = *current;

        return backingList<V,Vcount...>::list;
    }

}

template <typename It>
std::initializer_list<typename It::value_type> range_to_initializer_list(It begin, It end)
{
    return __range_to_initializer_list::generate_n(begin, end, begin);
}

int main()
{
    std::vector<int> vec = {1,2,3,4,5,6,7,8,9,10};
    std::initializer_list<int> list = range_to_initializer_list(vec.begin(), vec.end());
    for (int i : list)
        std::cout << i << std::endl;
    return 0;
}
于 2015-08-26T02:15:23.257 に答える
0

コピーを気にしない場合は、次のようなものがうまくいくと思います。

template<class Iterator>
using iterator_init_list = std::initializer_list<typename std::iterator_traits<Iterator>::value_type>;

template<class Iterator, class... Ts>
iterator_init_list<Iterator> to_initializer_list(Iterator start, Iterator last, Ts... xs)
{
    if (start == last) return iterator_init_list<Iterator>{xs...};
    else return to_initializer_list(start+1, last, xs..., *start);
}
于 2016-10-03T06:10:07.447 に答える
0

イテレータを返す2つのメソッドを使用してベクトルを渡すために、あいまいなイテレータクラスをテンプレート化せずに最善の解決策は、ベクトルを取る関数に関数ロジックを実装することだと思います。

void someThing(std::initializer_list<int> items)
{
     std::vector<int> v;
     for(int i:items)
     {
             v.push_back(i);
     }
     someThing(v);
}

void someThing(std::vector<int> items)
{
...
}
于 2020-11-18T11:01:10.170 に答える