Инициализация смешанного списка в C++11

Что касается инициализации списка c++11, могу ли я инициализировать список с помощью элемент и другой список?

Допустим, у меня есть следующий код:

#include <vector>

class Foo
{
    public:
        Foo(int value){m_v=value;}
    private:
        int m_v = 0;
};

int main()
{
   std::vector<Foo> v1, v2, v3;
   v1 = {Foo(1)}; //ok
   v2 = {Foo(2), Foo(3)}; //ok
   v3 = {Foo(3), v2}; //error: no match for ‘operator=’ (operand types are ‘std::vector’ and ‘’)
}

Есть ли способ создать в одной строке кода, используя инициализацию списка, вектор, состоящий из элемента другого вектора плюс новый элемент (в начале, в примере выше).


person ABCplus    schedule 01.10.2018    source источник
comment
Вы можете поместить любое количество утверждений в одну строку. Или напишите для этого свою функцию. В любом случае, вы выполняете назначение, а не инициализацию...   -  person Deduplicator    schedule 01.10.2018
comment
См. stackoverflow.com/a/18897049/3002139.   -  person Baum mit Augen    schedule 01.10.2018


Ответы (2)


Мы можем создать некоторую инфраструктуру шаблонов, позволяющую создавать векторы посредством необязательной конкатенации объектов и других векторов.

Это очень первый разрез:

#include <utility>
#include <vector>

namespace extended
{
    template<class T>
    struct appender
    {
        template<class V, class A, class Arg>
        void operator()(std::vector<V, A>& vec, Arg&& arg) const
        {
            vec.push_back(std::forward<Arg>(arg));
        }
    };

    template<class V2, class A2>
    struct appender<std::vector<V2, A2>>
    {
        template<class V, class A, class X>
        void operator()(std::vector<V, A>& vec, X&& arg) const
        {
            vec.insert(end(vec), begin(std::forward<X>(arg)), end(std::forward<X>(arg)));
        }
    };

    template<class V, class A, class T>
    auto append(std::vector<V, A>& target, T&& x) -> decltype(auto)
    {
        auto op = appender<std::decay_t<T>>();
        op(target, std::forward<T>(x));
        return target;
    }
}

template<class T, class...Args>
auto make_vector(Args&&...args)
{
    using extended::append;
    std::vector<T> result;
    using expand = int[];
    expand {0,
        (append(result, std::forward<Args>(args)), 0)...
    };
    return result;
}

class Foo
{
    public:
        Foo(int value){m_v=value;}
    private:
        int m_v = 0;
};

int main()
{
   auto v1 = make_vector<Foo>(Foo(1)); //ok
   auto v2 = make_vector<Foo>(Foo(2), Foo(3)); //ok
   auto v3 = make_vector<Foo>(Foo(3), v2); //ok
}

Конечно, ища общие интерфейсы, мы можем немного раздвинуть границы:

#include <utility>
#include <iterator>
#include <vector>
#include <list>
#include <set>

namespace extended
{
    // The general case of an appender.
    // simply calls emplace_back
    template<class T, class Diff = void>
    struct appender
    {
        template<class V, class A, class Arg>
        void operator()(std::vector<V, A>& vec, Arg&& arg) const
        {
            vec.emplace_back(std::forward<Arg>(arg));
        }
    };

    // specific specialisation for an appender where the
    // source object supports begin() and end() (i.e. a container)
    //
    template<class T>
    struct appender
    <
        T, 
        decltype(
            std::begin(std::declval<T>()), 
            std::end(std::declval<T>()),
            void()
        )
    >
    {
        template<class V, class A, class X>
        void operator()(std::vector<V, A>& vec, X&& arg) const
        {
            vec.insert(std::end(vec), std::begin(std::forward<X>(arg)), std::end(std::forward<X>(arg)));
        }
    };

    template<class V, class A, class T>
    auto append(std::vector<V, A>& target, T&& x) -> decltype(auto)
    {
        auto op = appender<std::decay_t<T>>();
        op(target, std::forward<T>(x));
        return target;
    }
}

template<class T, class...Args>
auto make_vector(Args&&...args)
{
    using extended::append;
    std::vector<T> result;
    using expand = int[];
    expand {0,
        (append(result, std::forward<Args>(args)), 0)...
    };
    return result;
}

class Foo
{
    public:
        Foo(int value){m_v=value;}

        bool operator<(const Foo& r) const { return m_v < r.m_v; }
    private:
        int m_v = 0;
};



int main()
{
   auto v1 = make_vector<Foo>(Foo(1)); //ok
   auto v2 = make_vector<Foo>(Foo(2), Foo(3)); //ok
   auto v3 = make_vector<Foo>(Foo(3), v2); //ok
   auto v4 = make_vector<Foo>(Foo(1), 
    std::list<Foo> { Foo(2), Foo(3) }, 
    make_vector<Foo>(4, make_vector<Foo>(8, 9, 10)),
    std::set<Foo> {Foo(6), Foo(7) }); // bizzare but ok
}
person Richard Hodges    schedule 01.10.2018
comment
Мои знания C++ позволяют мне понять 20% кода, но я проверил его, и он отлично работает! :-) - person ABCplus; 01.10.2018

std::vector<Foo> означает std::vector из Foo экземпляров. Это означает, что он не может произвольно хранить другие экземпляры std::vector, о чем вы просите компилятор при написании

v3 = {Foo(3), v2};

std::initializer_list<T> — это однородная коллекция T экземпляров. Конструктор списка std::vector<Foo> принимает std::initializer_list<Foo>. Невозможно добиться того, чего вы хотите, без ручной распаковки элементов v2 внутри фигурных скобок.


Есть ли способ создать в одной строке кода, используя инициализацию списка, вектор, состоящий из элемента другого вектора плюс новый элемент (в начале, в примере выше).

Использование инициализации списка, нет. Однако вы можете написать свою собственную функцию для достижения того же результата.

person Vittorio Romeo    schedule 01.10.2018