在 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};
- 编辑 -
正如 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) << ' '; }
}
您正在传递三个 lambda 函数来初始化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");
}
overloaded
和我能想象的最好的 C++14 替代方案(也遵循 bolov 对“make”函数的建议和他的递归示例)。
#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 版本更简单、更优雅。