获取变体的值,它本身可能是另一个变体
Get the value of a variant, which itself could be another variant
我有一个变体ScalarVar
using ScalarVar = std::variant<int, std::string>;
还有一个变体 Var
,它本身可以是 ScalarVar
或 std::vector
的 ScalarVar
s
using Var = std::variant<ScalarVar, std::vector<ScalarVar>>;
我想创建一个函数 template<typename T, typename Variant> T Get(const Variant& var);
,当给定一个不包含内部变体的变体时,它将像 std::get<T>
一样工作,即它将 return 的值 T
如果Variant
当前包含一个T
,或者如果给定一个包含其他变体的变体,它将递归地获取包含的类型,直到找到一个非变体,然后return那个.
这是我迄今为止最好的尝试:
#include <iostream>
#include <variant>
#include <string>
#include <typeindex>
#include <vector>
#include <map>
template<typename T, typename... T2>
struct is_variant { static inline constexpr bool value = false; };
template<typename... T>
struct is_variant<std::variant<T...>> { static inline constexpr bool value = true; };
template<typename T, typename Variant>
T Get(const Variant& var) {
static_assert (is_variant<Variant>::value == true, "Template parameter Variant must be a std::variant");
auto inner = std::visit([](const auto& i){ return i; }, var);
if constexpr(is_variant<typeof(inner)>::value) {
return Get<T>(inner);
}
else return inner;
}
int main()
{
using ScalarVar = std::variant<int, std::string>;
using Var = std::variant<ScalarVar, std::vector<ScalarVar>>;
ScalarVar s = 5;
std::cout << Get<int>(s) << std::endl;
return 0;
}
如果 T
不是 std::variant
,这应该只是 return T
,如果 [=21=,则 return std::get<InnerT>
] 是包含类型 T
.
的 std::variant
但是我从 gcc 得到一个非常复杂的编译错误:
std::cout << Get<int>(s) << std::endl
/usr/include/c++/9/variant:1005: error: invalid conversion from ‘std::__success_type<std::__cxx11::basic_string<char> >::type (*)(Get(const Variant&) [with T = int; Variant = std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >]::<lambda(const auto:22&)>&&, const std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)’ {aka ‘std::__cxx11::basic_string<char> (*)(Get(const Variant&) [with T = int; Variant = std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >]::<lambda(const auto:22&)>&&, const std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)’} to ‘int (*)(Get(const Variant&) [with T = int; Variant = std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >]::<lambda(const auto:22&)>&&, const std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)’ [-fpermissive]
1005 | { return _Array_type{&__visit_invoke}; }
| ^
| |
| std::__success_type<std::__cxx11::basic_string<char> >::type (*)(Get(const Variant&) [with T = int; Variant = std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >]::<lambda(const auto:22&)>&&, const std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&) {aka std::__cxx11::basic_string<char> (*)(Get(const Variant&) [with T = int; Variant = std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >]::<lambda(const auto:22&)>&&, const std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)}
我怎样才能获得所需的行为?
问题出在这一行:
std::visit([](const auto& i){ return i; }, var);
直接从 main
和 ScalarVar var = 5;
调用它会产生类似的消息。
让我们看看 cppreference 关于 std::visit
(C++17) 的 return 类型的说法:
The return type is deduced from the returned expression as if by
decltype. The call is ill-formed if the invocation above is not a
valid expression of the same type and value category, for all
combinations of alternative types of all variants.
这意味着你不能像上面那样调用std::visit
,因为它必须return不同的类型(int
/ std::string
)取决于里面的值。
您可以使用函数重载并为 std::variant
和其他类型使用两个不同的函数:
template <typename T, typename U>
T Get(U const& value)
{
if constexpr (std::is_same_v<T, U>)
{
return value;
}
// If you get here, none of the nested variants had a value of type T.
return T();
}
template <typename T, typename... Args>
T Get(std::variant<Args...> const& var)
{
return std::visit(
[](auto const& value) { return Get<T>(value); },
var
);
}
调用std::visit
.
时需要区分T
、std::variant<...>
和其他所有内容
template<typename T>
struct Getter {
T operator()(const T & t) { return t; }
template <typename ... Ts>
T operator()(const std::variant<Ts...> & var) { return std::visit(*this, var); }
template <typename U>
T operator()(U) { throw std::bad_variant_access(); }
};
template<typename T, typename Variant>
T Get(const Variant& var) {
Getter<T> getter;
return getter(var);
}
/* or
template <typename T>
inline Getter<T> Get;
*/
我有一个变体ScalarVar
using ScalarVar = std::variant<int, std::string>;
还有一个变体 Var
,它本身可以是 ScalarVar
或 std::vector
的 ScalarVar
s
using Var = std::variant<ScalarVar, std::vector<ScalarVar>>;
我想创建一个函数 template<typename T, typename Variant> T Get(const Variant& var);
,当给定一个不包含内部变体的变体时,它将像 std::get<T>
一样工作,即它将 return 的值 T
如果Variant
当前包含一个T
,或者如果给定一个包含其他变体的变体,它将递归地获取包含的类型,直到找到一个非变体,然后return那个.
这是我迄今为止最好的尝试:
#include <iostream>
#include <variant>
#include <string>
#include <typeindex>
#include <vector>
#include <map>
template<typename T, typename... T2>
struct is_variant { static inline constexpr bool value = false; };
template<typename... T>
struct is_variant<std::variant<T...>> { static inline constexpr bool value = true; };
template<typename T, typename Variant>
T Get(const Variant& var) {
static_assert (is_variant<Variant>::value == true, "Template parameter Variant must be a std::variant");
auto inner = std::visit([](const auto& i){ return i; }, var);
if constexpr(is_variant<typeof(inner)>::value) {
return Get<T>(inner);
}
else return inner;
}
int main()
{
using ScalarVar = std::variant<int, std::string>;
using Var = std::variant<ScalarVar, std::vector<ScalarVar>>;
ScalarVar s = 5;
std::cout << Get<int>(s) << std::endl;
return 0;
}
如果 T
不是 std::variant
,这应该只是 return T
,如果 [=21=,则 return std::get<InnerT>
] 是包含类型 T
.
但是我从 gcc 得到一个非常复杂的编译错误:
std::cout << Get<int>(s) << std::endl
/usr/include/c++/9/variant:1005: error: invalid conversion from ‘std::__success_type<std::__cxx11::basic_string<char> >::type (*)(Get(const Variant&) [with T = int; Variant = std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >]::<lambda(const auto:22&)>&&, const std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)’ {aka ‘std::__cxx11::basic_string<char> (*)(Get(const Variant&) [with T = int; Variant = std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >]::<lambda(const auto:22&)>&&, const std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)’} to ‘int (*)(Get(const Variant&) [with T = int; Variant = std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >]::<lambda(const auto:22&)>&&, const std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)’ [-fpermissive]
1005 | { return _Array_type{&__visit_invoke}; }
| ^
| |
| std::__success_type<std::__cxx11::basic_string<char> >::type (*)(Get(const Variant&) [with T = int; Variant = std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >]::<lambda(const auto:22&)>&&, const std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&) {aka std::__cxx11::basic_string<char> (*)(Get(const Variant&) [with T = int; Variant = std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >]::<lambda(const auto:22&)>&&, const std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)}
我怎样才能获得所需的行为?
问题出在这一行:
std::visit([](const auto& i){ return i; }, var);
直接从 main
和 ScalarVar var = 5;
调用它会产生类似的消息。
让我们看看 cppreference 关于 std::visit
(C++17) 的 return 类型的说法:
The return type is deduced from the returned expression as if by decltype. The call is ill-formed if the invocation above is not a valid expression of the same type and value category, for all combinations of alternative types of all variants.
这意味着你不能像上面那样调用std::visit
,因为它必须return不同的类型(int
/ std::string
)取决于里面的值。
您可以使用函数重载并为 std::variant
和其他类型使用两个不同的函数:
template <typename T, typename U>
T Get(U const& value)
{
if constexpr (std::is_same_v<T, U>)
{
return value;
}
// If you get here, none of the nested variants had a value of type T.
return T();
}
template <typename T, typename... Args>
T Get(std::variant<Args...> const& var)
{
return std::visit(
[](auto const& value) { return Get<T>(value); },
var
);
}
调用std::visit
.
T
、std::variant<...>
和其他所有内容
template<typename T>
struct Getter {
T operator()(const T & t) { return t; }
template <typename ... Ts>
T operator()(const std::variant<Ts...> & var) { return std::visit(*this, var); }
template <typename U>
T operator()(U) { throw std::bad_variant_access(); }
};
template<typename T, typename Variant>
T Get(const Variant& var) {
Getter<T> getter;
return getter(var);
}
/* or
template <typename T>
inline Getter<T> Get;
*/