std :: visit如何使用std :: variant?

时间:2017-12-23 21:53:40

标签: c++ c++17

我在这里查看std:variant/std::visit doc:http://en.cppreference.com/w/cpp/utility/variant/visit并且还搜索了很多,试图了解std::visitstd::variant背后的魔力。

所以我的问题如下。在提供的示例中,在多态lambda 和“overloaded”中都发生了一些“魔术”,这使得从std::variant中提取正确的类型成为可能。

所以看看这个:

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

对于每个v,这只是一个变体,如何调用正确的重载lambda函数?似乎有一些逻辑需要弄清楚特定std::variant所持有的确切类型,将其转换并将其分派给适当的函数。我的问题是它是如何工作的?同样的交易:

    std::visit([](auto&& arg) {
        using T = std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<T, int>)
            std::cout << "int with value " << arg << '\n';
        else if constexpr (std::is_same_v<T, long>)
            std::cout << "long with value " << arg << '\n';
        else if constexpr (std::is_same_v<T, double>)
            std::cout << "double with value " << arg << '\n';
        else if constexpr (std::is_same_v<T, std::string>)
            std::cout << "std::string with value " << std::quoted(arg) << '\n';
        else 
            static_assert(always_false<T>::value, "non-exhaustive visitor!");
    }, w);

我们将多态lambda作为可调用对象传递给访问者,而w是一个可以包含int,long,double或std :: string的变体。在哪个逻辑中找出正确的类型,以便using T = std::decay_t<decltype(arg)>;检索变体的特定实例的实际类型?

1 个答案:

答案 0 :(得分:4)

我认为,std::visit在引擎盖下构建了一个函数指针数组(在编译时),该数组由用于每种类型的实例化函数指针组成。该变体存储运行时类型索引i(整数),从而可以选择第i个正确的函数指针并插入值。

您可能想知道我们如何在编译时间数组中存储具有不同参数类型的函数指针? ->这是通过类型擦除(我认为)完成的,这意味着 一个存储功能,例如void*自变量,例如&A<T>::call

template<typename T>
static A
{
   static call(void*p) { otherFunction(static_cast<T*>(p)); } 
}

其中每个call都使用参数将其分派到正确的函数otherFunction(这是最后的lambda)。 类型擦除意味着功能auto f = &A<T>::call不再具有类型T的概念,而具有签名void(*)(void*)

std::variant确实很复杂而且非常复杂,因为许多强大的高级元编程技巧都在发挥作用。这个答案可能只涵盖冰山一角:-)