Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 858
The Vee Avatar asked Oct 03 '17 08:10

The Vee


People also ask

What is the point of std :: variant?

The class template std::variant represents a type-safe union. An instance of std::variant at any given time either holds a value of one of its alternative types, or in the case of error - no value (this state is hard to achieve, see valueless_by_exception).

How does C++ variant work?

It holds one of several alternatives in a type-safe way. No extra memory allocation is needed. The variant needs the size of the max of the sizes of the alternatives, plus some little extra space for knowing the currently active value. By default, it initializes with the default value of the first alternative.

What is STD Monostate?

std::monostate is a class that has exactly one value. It is default constructable and supports all the comparison operations. std::monostate is about as simple of a type as one could concoct. These properties turn out to be useful for writing template code. The first use case is in testing.

What is STD visit?

std::visit from C++17 is a powerful utility that allows you to call a function over a currently active type in std::variant . In this post, I'll show you how to leverage all capabilities of this handy function: the basics, applying on multiple variants, and passing additional parameters to the matching function.


2 Answers

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;
}
like image 192
cbuchart Avatar answered Nov 11 '22 06:11

cbuchart


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});
like image 30
Rakete1111 Avatar answered Nov 11 '22 04:11

Rakete1111