Reputation: 5201
I found the following code (by phoeen) instanciating a variant based on index at runtime:
template <typename... Ts>
std::variant<Ts...> make_variant(std::size_t i)
{
assert(i < sizeof...(Ts));
static constexpr auto table = std::array{ +[](){ return std::variant<Ts...>{Ts{ }}; }... };
return table[i]();
}
It works (for me, c++17, gcc 8.5), but I don't understand the instantiation of the array table
:
+
before the lambda ?Upvotes: 1
Views: 131
Reputation: 117298
- Why
std::array
has no template types here ?
C++17 added class template argument deduction (CTAD) which makes it possible to deduce the class template template parameters from the supplied arguments, either via implicit guides or by user provided deduction guides. For std::array
there is one user provided deduction guide:
template< class T, class... U >
array( T, U... ) -> array<T, 1 + sizeof...(U)>;
That is, if you create an array with std::array{1, 2, 3}
, the T
will deduced to int
and 1 + sizeof...(U)
(1 + 2) will be the non-type size argument of the std::array
, so it becomes std::array<int, 3>
.
- What is the goal of the
+
before the lambda ?
Each lambda becomes a unique type, but the unary plus can be used on a non-capturing lambda to get a regular function pointer to it. In this case, all the lambdas will have the type std::variant<Ts...>(*)()
, that is, a pointer to a function taking no arguments and returning std::variant<Ts...>
.
- How this fold expression works ?
It's not a fold expression but parameter expansion.
For each of the Ts...
, create a lamba that returns a std::variant<Ts...>
initialized with the specific Ts
type. Example:
auto v = make_variant<int, double>(1);
Will instantiate this function:
std::variant<int, double> make_variant(std::size_t i) {
static constexpr std::array<std::variant<int, double>(*)(), 2> table{
+[]() { return std::variant<int, double>{int{}}; },
+[]() { return std::variant<int, double>{double{}}; },
};
return table[i](); // return the variant initialized at pos `i`
}
Upvotes: 8
Reputation: 62694
table
is an array of pointer-to-functions of type std::variant<Ts...>(*)()
, i.e. ways of creating std::variant<Ts...>
from no arguments. Each entry in the array points to a function that default constructs one of the pack Ts
, and then constructs a variant from that.
One doesn't need to specify the type (or size) of the template parameters to std::array
, because of a language rule called class template argument deduction (CTAD).
The unary plus is to coerce the lambda expressions into pointer-to-functions, as a captureless lambda has an implicit conversion to pointer-to-function
There isn't a fold expression here, it is an ordinary pack expansion. A fold expression is the expansion of a pack with a binary operator in between each element. Each of the Ts
creates a function.
Upvotes: 1