What are the two lines declaring overloaded, just above int main(), mean?
The first one
template<class... Ts>
struct overloaded : Ts...
{ using Ts::operator()...; };
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 inherited operator(). This is an example of Variadic CRTP.
Unfortunately the variadic using is available only starting from C++17.
The second one
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
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
auto ov = overloaded{ arg1, arg2, arg3, arg4 };
or also
overloaded ov{ arg1, args, arg3, arg4 };
ov becomes an overloaded<decltype(arg1), decltype(arg2), decltype(arg3), decltype(arg4)>
This permits you to write something as
overloaded
{
[](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; },
}
that in C++14 was
auto l1 = [](auto arg) { std::cout << arg << ' '; };
auto l2 = [](double arg) { std::cout << std::fixed << arg << ' '; };
auto l3 = [](const std::string& arg) { std::cout << std::quoted(arg) << ' '; }
overloaded<decltype(l1), decltype(l2), decltype(l3)> ov{l1, l2, l3};
-- 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
overloaded
{
[](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; }
}
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
#include <iostream>
template <typename ... Ts>
struct overloaded : public Ts ...
{ using Ts::operator()...; };
template <typename ... Ts> overloaded(Ts...) -> overloaded<Ts...>;
int main ()
{
overloaded ov
{
[](auto arg) { std::cout << "generic: " << arg << std::endl; },
[](double arg) { std::cout << "double: " << arg << std::endl; },
[](long arg) { std::cout << "long: " << arg << std::endl; }
};
ov(2.1);
ov(3l);
ov("foo");
}
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.
#include <iostream>
template <typename ...>
struct overloaded;
template <typename T0>
struct overloaded<T0> : public T0
{
template <typename U0>
overloaded (U0 && u0) : T0 { std::forward<U0>(u0) }
{ }
};
template <typename T0, typename ... Ts>
struct overloaded<T0, Ts...> : public T0, public overloaded<Ts ...>
{
using T0::operator();
using overloaded<Ts...>::operator();
template <typename U0, typename ... Us>
overloaded (U0 && u0, Us && ... us)
: T0{std::forward<U0>(u0)}, overloaded<Ts...> { std::forward<Us>(us)... }
{ }
};
template <typename ... Ts>
auto makeOverloaded (Ts && ... ts)
{
return overloaded<Ts...>{std::forward<Ts>(ts)...};
}
int main ()
{
auto ov
{
makeOverloaded
(
[](auto arg) { std::cout << "generic: " << arg << std::endl; },
[](double arg) { std::cout << "double: " << arg << std::endl; },
[](long arg) { std::cout << "long: " << arg << std::endl; }
)
};
ov(2.1);
ov(3l);
ov("foo");
}
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.