C++17 std::visit 示例中令人困惑的模板

2023-12-26

当看着std::visit()cppreference 中的页面,https://en.cppreference.com/w/cpp/utility/variant/visit https://en.cppreference.com/w/cpp/utility/variant/visit,我遇到了我无法理解的代码......

这是缩写版本:

#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);
    }
}

这两行声明了什么overloaded,就在上面int main(), mean?

谢谢您的解释!

2019年新增
在下面两位先生提供了详细的解释之后(非常感谢!),我在这本非常好的书中偶然发现了相同的代码C++17 详细信息 - 了解新 C++ 标准的令人兴奋的功能!作者:巴特沃米耶·菲利佩克。写得这么好的书!


int main() 上方声明重载的两行是什么意思?

第一个

template<class... Ts>
struct overloaded : Ts... 
 { using Ts::operator()...; };

是一个经典的类/结构声明/定义/实现。从 C++11 起有效(因为使用可变参数模板)。

在这种情况下,overloaded继承自所有模板参数并启用 (using行)全部继承operator()。这是一个例子可变 CRTP https://isocpp.org/blog/tag/crtp/P100.

不幸的是,可变参数using仅从 C++17 开始可用。

第二个

template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

是一个“演绎指南”(参见这一页 https://en.cppreference.com/w/cpp/language/class_template_argument_deduction了解更多详细信息),这是一个新的 C++17 功能。

就你而言,演绎指南说,当你写一些东西时

auto ov = overloaded{ arg1, arg2, arg3, arg4 };

or also

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");      
 }

以及最好的 C++14 替代方案(也遵循 bolov 的“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 版本更简单、更优雅。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++17 std::visit 示例中令人困惑的模板 的相关文章

随机推荐