Что означают две строки, объявляющие перегруженные чуть выше int main ()?
Первый
template<class... Ts>
struct overloaded : Ts...
{ using Ts::operator()...; };
является классическим объявлением / определением / реализацией класса / структуры.Действительно из C ++ 11 (потому что используют шаблоны с переменным числом аргументов).
В этом случае overloaded
наследует от всех параметров шаблона и разрешает (using
строка) все унаследованные operator()
.Это пример Variadic CRTP .
К сожалению, вариант using
доступен только начиная с C ++ 17.
Второй
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
- это «руководство по выводам» (подробнее см. на этой странице ), и это новая функция C ++ 17.
В вашем случае руководство по выводу говорит, что когда вы пишете что-то как
auto ov = overloaded{ arg1, arg2, arg3, arg4 };
или также
overloaded ov{ arg1, args, arg3, arg4 };
ov
становится overloaded<decltype(arg1), decltype(arg2), decltype(arg3), decltype(arg4)>
Это позволяет вам написать что-то как
overloaded
{
[](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; },
}
, что в C ++ 14 было
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 -
Как указывает Nemo (спасибо!) В примере кода в вашем вопросе, есть еще одна интересная новая особенность C ++ 17: агрегатная инициализация базовых классов.
Я имею в виду ... когда вы пишете
overloaded
{
[](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; }
}
вы передаете три лямбда-функции для инициализации трех базовых классов overloaded
.
До C ++ 17 вы могли сделать это, только если вы написали явный конструктор для этого,Начиная с C ++ 17, он работает автоматически.
На данный момент, мне кажется, что может быть полезно показать упрощенный полный пример вашего overloaded
на C ++ 17 и соответствующего CПример ++ 14.
Я предлагаю следующую программу C ++ 17
#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");
}
и лучшую альтернативу C ++ 14 (следуя также предложению Болова о функции make иего рекурсивный overloaded
пример), который я могу себе представить.
#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");
}
Я полагаю, что это вопрос мнения, но мне кажется, что версия C ++ 17 намного проще и элегантнее.