问题
Earlier I asked this question about std::variant
. Considering that the types hold by the variant are all printable by std::cout
, is there a simple way to implement a visitor?
Here for example, all the way down you have several lambdas to cover each type, but all do the same thing (except std::string
): std::cout << arg << ' ';
. Is there a way to not repeat my self?
std::visit(overloaded {
[](int arg) { std::cout << arg; },
[](long arg) { std::cout << arg; },
[](double arg) { std::cout << arg; }
// I removed the std::string case
}, v); // v is the std::variant
and write instead:
std::visit( [](auto arg) { std::cout << arg; }, v);
or something like:
template<typename T>
void printer(T arg) {std::cout << arg; }
//.......
std::visit(printer, v);
回答1:
No need to copy
std::visit( [](auto&& arg) { std::cout << arg; }, v);
this takes arg
by (forwarding) reference. I don't bother forwarding it; I don't care if it is an rvalue or lvalue really.
The template function doesn't work, because visit requires an object, and template functions aren't objects of functions; you cannot (yet) pass overload set names as objects in C++.
The overload
trick is mainly when you want to dispatch different behavior.
One thing you can do is
template<typename T>
void printer(T arg) {std::cout << arg; }
std::visit([](auto&&arg){printer(arg);}, v);
or
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype( __VA_ARGS__ )
#define OVERLOADS_OF(...) \
[](auto&&...args) \
RETURNS( __VA_ARGS__(decltype(args)(args)...) ) \
{ return __VA_ARGS__(decltype(args)(args)...); }
then we get:
template<typename T>
void printer(T arg) {std::cout << arg; }
std::visit(OVERLOADS_OF(printer), v);
which creates an anonymous object that represents the overload set of functions named by the token printer
.
来源:https://stackoverflow.com/questions/44058784/would-a-template-work-for-stdvariants-visit