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

3

8

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.

Headsail answered 3/10, 2017 at 8:41 Comment(2)
If it consoles you, std::make_unique and family have the same limitation :(Tammy
aggregate initialization has always been a thorn in the C++ initialization systemFerrotype
T
2

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});
Tammy answered 3/10, 2017 at 8:52 Comment(0)
R
3

Maybe it is not exactly what you are asking, but what about explicitly constructing the object instead of relying on type inference?

#include <variant>

struct x {
  int y;
};

int main() {
  std::variant<x> v(std::in_place_type<x>, x{3});
  return std::get<x>(v).y;
}
Rooftop answered 3/10, 2017 at 8:52 Comment(2)
Actually I'd be happy with this! My question, per title, is about avoiding the constructor, which this achieves. It will technically involve some moving around but in simple cases the optimizer will take care of that with no overhead.Headsail
Indeed it would be straightforward if your class provides a moving constructor. As you don't want any explicitly defined constructor you should guarantee the implicit moving constructor (or defaulting one at least).Rooftop
T
2

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});
Tammy answered 3/10, 2017 at 8:52 Comment(0)
J
0

If you want to go overkill, we can create a factory type that has a conversion operator:

template <class... Args>
struct list_init_from {
    std::tuple<Args...> args;

    template <class T>
    operator T() {
        return std::apply([](auto... args){
            return T{args...};
        }, args);
    }   
};

template <class... Args>
list_init_from(Args... ) -> list_init_from<Args...>;

Which you can use:

std::variant<x> v(std::in_place_type<x>, list_init_from{3});

This works, but leaves much to be desired: perfect forwarding, SFINAE on the conversion operator, and explicitly specifying which types to allow conversions to are exercises left to the reader.

Jackpot answered 3/10, 2017 at 15:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.