Default-construct all the types in a std::variant and put them in a std::vector

1.2k Views Asked by At

How would I make a std::vector that contains a default-constructed instance of all the types contained in a std::variant?

using TaskVariant = std::variant<TypeA, TypeB, TypeC>;
std::vector<TaskVariant> variants;

// I'd like to do this in a loop
variants.push_back(TypeA());
variants.push_back(TypeB());
variants.push_back(TypeC());
4

There are 4 best solutions below

2
On BEST ANSWER

1.Solution: You can convert variant to tuple and then use C++17 std::apply:

#include <variant>
#include <iostream>
#include <string>
#include <vector>
#include <tuple>

using namespace std;

template <typename... Types>
struct ToTuple;

template <typename... Types>
struct ToTuple<variant<Types...>>
{
    using type = tuple<Types...>;
};

struct TypeA {};
struct TypeB {};
struct TypeC {};

using TaskVariant = std::variant<TypeA, TypeB, TypeC>;

int main()
{
    vector<TaskVariant> vec;
    apply([&vec](auto&&... args) {(vec.push_back(args), ...); }, ToTuple<TaskVariant>::type{});
}

EDIT:

2.Solution: Using make_index_sequence, variant_alternative_t and variant_size_v (all C++17) (thanks to @s d by pointing out to variant_alternative_t, but I have modified example by using make_index_sequence instead of recursion):

#include <variant>
#include <iostream>
#include <string>
#include <vector>
using namespace std;

struct TypeA {};
struct TypeB {};
struct TypeC {};

using TaskVariant = std::variant<TypeA, TypeB, TypeC>;

template <size_t... Idx>
void fill_vec(vector<TaskVariant>& vec, index_sequence<Idx...> idx)
{
    (vec.push_back(variant_alternative_t<Idx, TaskVariant>{}), ...);
}

int main()
{
    vector<TaskVariant> vec;
    fill_vec(vec, make_index_sequence<variant_size_v<TaskVariant>>{});
}
0
On

Here sample solution using variadic templates.

#include <vector>
#include <variant>
#include <iostream>
#include <typeinfo>

struct TypeA {};
struct TypeB {};
struct TypeC {};

using TaskVariant = std::variant<TypeA, TypeB, TypeC>;

template <class T>
void addValue(std::vector<TaskVariant> & v)
{
 v.push_back(T());
 std::cout << "adding " << typeid(T).name() << " default ctor\n";
}

template <class T, class V, class... Args>
void addValue(std::vector<TaskVariant> & v)
{
  addValue<T>(v);
  addValue<V, Args...>(v);
}

template <class... Args>
void addValues(std::vector<std::variant<Args...>> & v)
{
  addValue<Args...>(v);
}

int main()
{
  std::vector<TaskVariant> variants;
  addValues(variants);
}

Output is:

adding 5TypeA default ctor
adding 5TypeB default ctor
adding 5TypeC default ctor
1
On

Yet another solution

#include <iostream>
#include <variant>
#include <vector>

struct TypeA {
  int a;
  TypeA() : a{0} {std::cout<<"A"<<std::endl;};
};

struct TypeB {
  int b;
  TypeB() : b{0} {std::cout<<"B"<<std::endl;};
};

struct TypeC {
  int c;
  TypeC() : c{0} {std::cout<<"C"<<std::endl;};
};

using TaskVariant = std::variant<TypeA, TypeB, TypeC>;

template<class var, std::size_t I = 0>
void autofill(std::vector<var>& vec){
  if constexpr(I < std::variant_size_v<var>){
    vec.push_back(std::variant_alternative_t<I, var>{});
    autofill<var, I + 1>(vec);
  }
}


int main() {
  std::vector<TaskVariant> variants;

  autofill(variants);
}

Edit: completely forgot about this feature in C++17.

template<class... VA> //VA for variant alternatives
void autofill(std::vector<std::variant<VA...> >& vec) {
    (..., vec.emplace_back(VA{}));
}

https://en.cppreference.com/w/cpp/language/fold edit 2: seems like @max beat me to that about 4 hours ago ^^

4
On

What about using template folding (comma operator) over types?

#include <variant>
#include <vector>

struct TypeA {};
struct TypeB {};
struct TypeC {};

using TaskVariant = std::variant<TypeA, TypeB, TypeC>;

template <typename ... Ts>
void addTypes (std::vector<std::variant<Ts...>> & vec)
 { (vec.push_back(Ts{}), ...); }

int main()
 {
   std::vector<TaskVariant> vec;

   addTypes(vec);
 }