When looking at std::visit()
page in cppreference,
https://en.cppreference.com/w/cpp/utility/variant/visit, I encountered the code I can't make sense of...
Here's the abbreviated version:
#include <iomanip>
#include <iostream>
#include <string>
#include <type_traits>
#include <variant>
#include <vector>
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...)->overloaded<Ts...>;
int main() {
std::vector<std::variant<int,long,double,std::string>> vec = { 10, 15l, 1.5, "hello" };
for (auto& v : vec) {
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);
}
}
What do the two lines declaring overloaded
, just above int main()
, mean?
Thank you for explaining!
2019 Addition
After the two gentlemen below provided detailed explanations (thank you so much!), I've stumbled upon the same code in the very fine book C++17 in Detail -
Learn the Exciting Features of The New C++ Standard! by Bartłomiej Filipek. Such a well written book!
The first one
is a classic class/struct declaration/definition/implementation. Valid from C++11 (because use variadic templates).
In this case,
overloaded
inherits from all template parameters and enables (using
row) all inheritedoperator()
. This is an example of Variadic CRTP.Unfortunately the variadic
using
is available only starting from C++17.The second one
is a "deduction guide" (see this page for more details) and it's a new C++17 feature.
In your case, the deduction guide says that when you write something as
or also
ov
becomes anoverloaded<decltype(arg1), decltype(arg2), decltype(arg3), decltype(arg4)>
This permits you to write something as
that in C++14 was
-- EDIT --
As pointed by Nemo (thanks!) in the example code in your question there is another interesting new C++17 feature: the aggregate initialization of base classes.
I mean... when you write
you're passing three lambda functions to initialize three base classes of
overloaded
.Before C++17, you could do this only if you wrote an explicit constructor to do it. Starting from C++17, it works automatically.
At this point, it seems to me that it can be useful to show a simplified full example of your
overloaded
in C++17 and a corresponding C++14 example.I propose the following C++17 program
and the best C++14 alternative (following also the bolov's suggestion of a "make" function and his recursive
overloaded
example) that I can imagine.I suppose that it's matter of opinion, but it seems to me that the C++17 version is a lot simpler and more elegant.