Search code examples
c++templatesc++17template-meta-programmingstd-variant

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


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());

Solution

  • 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>>{});
    }