Mic*_*ski 30 c++ visitor variant switch-statement c++17
许多人使用C++ 17/boost变体的模式看起来与switch语句非常相似.例如:( 来自cppreference.com的片段)
std::variant<int, long, double, std::string> v = ...;
std::visit(overloaded {
[](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; },
}, v);
Run Code Online (Sandbox Code Playgroud)
问题是当您在访问者中输入错误的类型或更改变体签名时,但忘记更改访问者.您将获得错误的lambda,通常是默认的lambda,而不是获得编译错误,或者您可能会得到一个您没有计划的隐式转换.例如:
v = 2.2;
std::visit(overloaded {
[](auto arg) { std::cout << arg << ' '; },
[](float arg) { std::cout << std::fixed << arg << ' '; } // oops, this won't be called
}, v);
Run Code Online (Sandbox Code Playgroud)
关于枚举类的switch语句更安全,因为您不能使用不属于枚举的值来编写case语句.同样,我认为如果变体访问者仅限于变体中包含的类型的子集,加上默认处理程序,那将非常有用.有可能实现这样的东西吗?
编辑:s /隐式转换/隐式转换/
EDIT2:我想有一个有意义的catch-all [](auto)处理程序.我知道如果不处理变体中的每个类型,删除它将导致编译错误,但这也会从访问者模式中删除功能.
Hol*_*olt 25
如果您只想允许类型的子集,那么您可以static_assert在lambda的开头使用a ,例如:
template <typename T, typename... Args>
struct is_one_of:
std::disjunction<std::is_same<std::decay_t<T>, Args>...> {};
std::visit([](auto&& arg) {
static_assert(is_one_of<decltype(arg),
int, long, double, std::string>{}, "Non matching type.");
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, int>)
std::cout << "int with value " << arg << '\n';
else if constexpr (std::is_same_v<T, double>)
std::cout << "double with value " << arg << '\n';
else
std::cout << "default with value " << arg << '\n';
}, v);
Run Code Online (Sandbox Code Playgroud)
如果您在变体中添加或更改类型,或者添加一个类型,则这将失败,因为T需要恰好是给定类型之一.
您也可以使用您的变体std::visit,例如使用"默认"访问者,例如:
template <typename... Args>
struct visit_only_for {
// delete templated call operator
template <typename T>
std::enable_if_t<!is_one_of<T, Args...>{}> operator()(T&&) const = delete;
};
// then
std::visit(overloaded {
visit_only_for<int, long, double, std::string>{}, // here
[](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; },
}, v);
Run Code Online (Sandbox Code Playgroud)
如果添加一个类型,它是不是一个int,long,double或std::string,然后visit_only_for调用操作将被匹配,你将有一个模糊的调用(这一个,默认的之间).
这也应该没有默认值,因为visit_only_for调用运算符将匹配,但由于它被删除,您将得到编译时错误.