0

With reference to c++11 list initialization, may I initialize a list with an element and another list?

Let's say I have the following code:

#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 ‘’) } 

Is there a way to create in one line of code, using list initialization, a vector made of the element of another vector plus a new element (a prepend, in the example above).

2
  • You can put any number of statements on the same line. Or write your own function for that. Anyway, you are doing assignment, not initialization... Commented Oct 1, 2018 at 11:04
  • See stackoverflow.com/a/18897049/3002139 Commented Oct 1, 2018 at 11:05

2 Answers 2

1

We can create some template infrastructure to allow creation of vectors through optional concatenation of objects and other vectors.

This is very much a first cut:

#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 } 

Of course, by looking for common interfaces we can start to push the boundaries a little:

#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 } 
Sign up to request clarification or add additional context in comments.

1 Comment

My C++ knowledge make me understand the 20% of the code, but I've checked it and it's working great! :-)
1

std::vector<Foo> means a std::vector of Foo instances. This means it cannot arbitrarily store other std::vector instances, which is what you're asking the compiler when writing

v3 = {Foo(3), v2}; 

std::initializer_list<T> is a homogeneous collection of T instances. std::vector<Foo>'s list constructor takes std::initializer_list<Foo>. There's no way to achieve what you want without manually unpacking v2's elements inside the curly braces.


Is there a way to create in one line of code, using list initialization, a vector made of the element of another vector plus a new element (a prepend, in the example above).

Using list initialization, no. You can write your own function to achieve the same thing, however.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.