return 来自 std::variant 到 std::visit 中可能类型的值
return value from possible type in std::variant through std::visit
我正在努力思考 std::variant
和 std::visit
我正在尝试想出一种方法来指定我希望我的变量保存的几种类型(会进入我的 std::variant
),然后通过 std::visit
检索存储的数据。考虑以下示例:
#include <iostream>
#include <variant>
#include <string>
struct PrintType {
void operator()(const int &data) {
std::cout << "visiting int node" << std::endl;
}
void operator()(const double &data) {
std::cout << "visiting double node" << std::endl;
}
};
struct SingleOperatorOverload {
int operator()(const int &data) {
std::cout << "visiting int node" << std::endl;
return data;
}
};
struct AllTypesOperatorOverload {
int operator()(const int &data) {
std::cout << "visiting int node" << std::endl;
return data;
}
double operator()(const double &data) {
std::cout << "visiting double node" << std::endl;
return data;
}
};
int main() {
using var_t = std::variant<int, double>;
// print int related operator() content, OK
var_t foo = 42;
std::visit(PrintType(), foo);
// print double related operator() content, OK
foo = 3.1415;
std::visit(PrintType(), foo);
// get value and store into bar, struct with single operator(), OK
foo = 42;
auto bar = std::visit(SingleOperatorOverload(), foo);
std::cout << "bar: " << bar << std::endl;
// get value and store into bar, struct with multiple operator(), ERROR
auto bar = std::visit(AllTypesOperatorOverload(), foo);
std::cout << "bar: " << bar << std::endl;
return 0;
}
允许变体保留(在这个简化的示例中)int
或 double
。如果我只是想根据类型打印一些东西(就像 PrintType
结构所做的那样),那很好。
如果我想像 SingleOperatorOverload
class 中那样通过访问者检索数据,它只提供 operator()
接受 int 作为参数的实现,那行得通.但是,一旦我尝试为 std::variant
中的每种类型实现 operator()
,即这里的 int
和 double
,就像在 AllTypesOperatorOverload
结构中一样,我收到编译错误 error: invalid conversion from '...' {aka double ...} to '...' {aka int ...}
所以似乎 std::variant
处理函数签名的方式不同?
我尝试了 SFINAE,但这似乎并没有缓解问题
struct AllTypesOperatorOverload {
template<typename T, std::enable_if_t<std::is_same<T, int>::value>>
T operator()(const T &data) {
std::cout << "visiting int node" << std::endl;
return data;
}
template<typename T, std::enable_if_t<std::is_same<T, double>::value>>
T operator()(const T &data) {
std::cout << "visiting double node" << std::endl;
return data;
}
};
现在将报告 error: no type named 'type' in 'struct std::invoke_result<AllTypesOperatorOverload, int&>'
。有没有办法为所有类型提供 operator()
,然后根据 foo
的设置方式将它们各自的值接收到具有正确类型的 bar
中?我知道 std::get_if<T>()
在这里可能有用,但理想情况下,我不想对每种类型进行长 if 语句检查,除非绝对必要(这是一个简化的示例,我可能想要我的 std::variant
).
中还有更多类型
错误消息很糟糕,但这里的问题是变体的所有备选方案必须在访问者中具有相同的 return 类型。您的 AllTypesOperatorOverload
不遵守此规则,returning 一个 double
和一个 int
,它们不是同一类型。
libstdc++ or any version of libc++ produce much better error messages的最新版本明确告诉你这一点(以下是我绕过的词):
error: static_assert failed due to requirement '__visit_rettypes_match'
"std::visit requires the visitor to have the same return type for
all alternatives of a variant"
static_assert(__visit_rettypes_match,
这是有道理的,因为当你看这一行时,bar
的类型是什么?
auto bar = std::visit(AllTypesOperatorOverload(), foo);
如果您被允许 return 不同的类型,bar
的类型将取决于 foo
在运行时持有哪个替代项。这在 C++ 中行不通。
请注意,使用 lambda 表达式而不是外部定义的结构为 std::visit
创建访问者有更简单的方法。您可以使用 if constexpr
:
std::visit([](auto value) {
if constexpr (std::is_same_v<int, decltype(value)>) {
std::cout << "visiting int\n";
} else {
static_assert(std::is_same_v<double, decltype(value)>);
std::cout << "visiting double\n";
}
std::cout << "bar: " << value << '\n';
}, foo);
或者,您可以定义一个 overloaded
辅助结构,让您重载 lambda:
template <typename... Lambdas>
struct overloaded : Lambdas...
{
template <typename... Fns>
explicit constexpr overloaded(Fns&&... fns)
: Lambdas(std::forward<Fns>(fns))...
{}
using Lambdas::operator()...;
};
template <typename... Lambdas>
overloaded(Lambdas...) -> overloaded<Lambdas...>;
// Usage:
std::visit(overloaded{
[](int value) {
std::cout << "visiting int\n";
std::cout << "bar: " << value << '\n';
},
[](double value) {
std::cout << "visiting double\n";
std::cout << "bar: " << value << '\n';
}
}, foo);
我正在努力思考 std::variant
和 std::visit
我正在尝试想出一种方法来指定我希望我的变量保存的几种类型(会进入我的 std::variant
),然后通过 std::visit
检索存储的数据。考虑以下示例:
#include <iostream>
#include <variant>
#include <string>
struct PrintType {
void operator()(const int &data) {
std::cout << "visiting int node" << std::endl;
}
void operator()(const double &data) {
std::cout << "visiting double node" << std::endl;
}
};
struct SingleOperatorOverload {
int operator()(const int &data) {
std::cout << "visiting int node" << std::endl;
return data;
}
};
struct AllTypesOperatorOverload {
int operator()(const int &data) {
std::cout << "visiting int node" << std::endl;
return data;
}
double operator()(const double &data) {
std::cout << "visiting double node" << std::endl;
return data;
}
};
int main() {
using var_t = std::variant<int, double>;
// print int related operator() content, OK
var_t foo = 42;
std::visit(PrintType(), foo);
// print double related operator() content, OK
foo = 3.1415;
std::visit(PrintType(), foo);
// get value and store into bar, struct with single operator(), OK
foo = 42;
auto bar = std::visit(SingleOperatorOverload(), foo);
std::cout << "bar: " << bar << std::endl;
// get value and store into bar, struct with multiple operator(), ERROR
auto bar = std::visit(AllTypesOperatorOverload(), foo);
std::cout << "bar: " << bar << std::endl;
return 0;
}
允许变体保留(在这个简化的示例中)int
或 double
。如果我只是想根据类型打印一些东西(就像 PrintType
结构所做的那样),那很好。
如果我想像 SingleOperatorOverload
class 中那样通过访问者检索数据,它只提供 operator()
接受 int 作为参数的实现,那行得通.但是,一旦我尝试为 std::variant
中的每种类型实现 operator()
,即这里的 int
和 double
,就像在 AllTypesOperatorOverload
结构中一样,我收到编译错误 error: invalid conversion from '...' {aka double ...} to '...' {aka int ...}
所以似乎 std::variant
处理函数签名的方式不同?
我尝试了 SFINAE,但这似乎并没有缓解问题
struct AllTypesOperatorOverload {
template<typename T, std::enable_if_t<std::is_same<T, int>::value>>
T operator()(const T &data) {
std::cout << "visiting int node" << std::endl;
return data;
}
template<typename T, std::enable_if_t<std::is_same<T, double>::value>>
T operator()(const T &data) {
std::cout << "visiting double node" << std::endl;
return data;
}
};
现在将报告 error: no type named 'type' in 'struct std::invoke_result<AllTypesOperatorOverload, int&>'
。有没有办法为所有类型提供 operator()
,然后根据 foo
的设置方式将它们各自的值接收到具有正确类型的 bar
中?我知道 std::get_if<T>()
在这里可能有用,但理想情况下,我不想对每种类型进行长 if 语句检查,除非绝对必要(这是一个简化的示例,我可能想要我的 std::variant
).
错误消息很糟糕,但这里的问题是变体的所有备选方案必须在访问者中具有相同的 return 类型。您的 AllTypesOperatorOverload
不遵守此规则,returning 一个 double
和一个 int
,它们不是同一类型。
libstdc++ or any version of libc++ produce much better error messages的最新版本明确告诉你这一点(以下是我绕过的词):
error: static_assert failed due to requirement '__visit_rettypes_match' "std::visit requires the visitor to have the same return type for all alternatives of a variant" static_assert(__visit_rettypes_match,
这是有道理的,因为当你看这一行时,bar
的类型是什么?
auto bar = std::visit(AllTypesOperatorOverload(), foo);
如果您被允许 return 不同的类型,bar
的类型将取决于 foo
在运行时持有哪个替代项。这在 C++ 中行不通。
请注意,使用 lambda 表达式而不是外部定义的结构为 std::visit
创建访问者有更简单的方法。您可以使用 if constexpr
:
std::visit([](auto value) {
if constexpr (std::is_same_v<int, decltype(value)>) {
std::cout << "visiting int\n";
} else {
static_assert(std::is_same_v<double, decltype(value)>);
std::cout << "visiting double\n";
}
std::cout << "bar: " << value << '\n';
}, foo);
或者,您可以定义一个 overloaded
辅助结构,让您重载 lambda:
template <typename... Lambdas>
struct overloaded : Lambdas...
{
template <typename... Fns>
explicit constexpr overloaded(Fns&&... fns)
: Lambdas(std::forward<Fns>(fns))...
{}
using Lambdas::operator()...;
};
template <typename... Lambdas>
overloaded(Lambdas...) -> overloaded<Lambdas...>;
// Usage:
std::visit(overloaded{
[](int value) {
std::cout << "visiting int\n";
std::cout << "bar: " << value << '\n';
},
[](double value) {
std::cout << "visiting double\n";
std::cout << "bar: " << value << '\n';
}
}, foo);