Search code examples
c++c++17variantaggregate-initialization

Can I avoid explicitly writing a constructor for each struct in a std::variant?


Consider this code:

#include <variant>

struct x {
  int y;
};

int main() {
  std::variant<x> v(std::in_place_type<x>, {3}); /*1*/
  return std::get<x>(v).y;
}

This does not compile and neither does when removing the {} from the line /*1*/, even though aggregate initialization

x a{3};
x b({3});

works in both "constructor-like" forms. Can I somehow make the std::variant initializer aware of the possibility of constructing structs using aggregate initialization without having to write boring boilerplate constructors for each struct that may be used in my real-world case?

I would expect this to work, somehow, as per cppreference the two overloads (5) and (6) in question both say

Constructs a variant with the specified alternative T and initializes the contained value with the arguments [...]

I'm using GCC 7 if that matters.


Solution

  • There is no workaround for this, apart from adding a constructor. The standard mandates this for both overloads you mention, [variant.ctor]19 and [variant.ctor]23 respectively:

    Effects: Initializes the contained value as if direct-non-list-initializing an object of type T with the arguments std​::​forward<Args>(args)....

    Effects: Initializes the contained value as if direct-non-list-initializing an object of type T with the arguments il, std​::​forward<Args>(args)....

    You can always copy or move the object using:

    std::variant<x> v(std::in_place_type<x>, x{3});
    // or more clear and does the same thing
    std::variant<x> v(x{3});