I'm trying to access the content of a variant. I don't know what's in there, but thankfully, the variant does. So I thought I'll just ask the variant what index it is on and then use that index to std::get
its content.
But this does not compile:
#include <variant>
int main()
{
std::variant<int, float, char> var { 42.0F };
const std::size_t idx = var.index();
auto res = std::get<idx>(var);
return 0;
}
The error happens in the std::get
call:
error: no matching function for call to ‘get<idx>(std::variant<int, float, char>&)’
auto res = std::get<idx>(var);
^
In file included from /usr/include/c++/8/variant:37,
from main.cpp:1:
/usr/include/c++/8/utility:216:5: note: candidate: ‘template<long unsigned int _Int, class _Tp1, class _Tp2> constexpr typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(std::pair<_Tp1, _Tp2>&)’
get(std::pair<_Tp1, _Tp2>& __in) noexcept
^~~
/usr/include/c++/8/utility:216:5: note: template argument deduction/substitution failed:
main.cpp:9:31: error: the value of ‘idx’ is not usable in a constant expression
auto res = std::get<idx>(var);
^
main.cpp:7:15: note: ‘std::size_t idx’ is not const
std::size_t idx = var.index();
^~~
How can I fix this?
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).
Since this function is specific to a given type, you don't need RTTI to perform the operations required by std::any .
Boost. Variant, part of collection of the Boost C++ Libraries. It is a safe, generic, stack-based discriminated union container, offering a simple solution for manipulating an object from a heterogeneous set of types in a uniform manner.
The compiler needs to know the value of idx
at compilation time for std::get<idx>()
to work, because it is being used as a template argument.
First option: If the code is meant to run at compile time, then make everything constexpr
:
constexpr std::variant<int, float, char> var { 42.0f };
constexpr std::size_t idx = var.index();
constexpr auto res = std::get<idx>(var);
This works because std::variant
is constexpr
friendly (its constructors and methods are all constexpr
).
Second option: If the code is not meant to run at compile time, which is likely the case, the compiler cannot deduce at compile time the type of res
, because it could be three different things (int
, float
or char
). C++ is a statically-typed language, and the compiler must be able to deduce the type of auto res = ...
from the expression that follows (i.e. it must always be the same type).
You can use std::get<T>
, with the type instead of an index, if you already know what it will be:
std::variant<int, float, char> var { 42.0f }; // chooses float
auto res = std::get<float>(var);
In general, use std::holds_alternative
to check if the variant is holding each of the given types, and handle them separately:
std::variant<int, float, char> var { 42.0f };
if (std::holds_alternative<int>(var)) {
auto int_res = std::get<int>(var); // int&
// ...
} else if (std::holds_alternative<float>(var)) {
auto float_res = std::get<float>(var); // float&
// ...
} else {
auto char_res = std::get<char>(var); // char&
// ...
}
Alternatively you can use std::visit
. This is slightly more complicated: you can use a lambda/templated function that is type-agnostic and works for all of the variant's types, or pass a functor with an overloaded call operator:
std::variant<int, float, char> var { 42.0f };
std::size_t idx = var.index();
std::visit([](auto&& val) {
// use val, which may be int&, float& or char&
}, var);
See std::visit for details and examples.
Essentially, you cannot.
You wrote:
I don't know what's in there, but thankfully, the variant does
... but only at run-time, not at compile-time.
And that means your idx
value is not compile-time.
And that means you can't use get<idx>()
directly.
Something you could do is have a switch statement; ugly, but it would work:
switch(idx) {
case 0: { /* code which knows at compile time that idx is 0 */ } break;
case 1: { /* code which knows at compile time that idx is 1 */ } break;
// etc. etc.
}
This is rather ugly however. As comments suggest, you might as well std::visit()
(which is not very different from the code above, except using variadic template arguments instead of being this explicit) and avoid the switch altogether. For other index-based approaches (not specific to std::variant
), see:
Idiom for simulating run-time numeric template parameters?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With