簡體   English   中英

獲取一個變體的值,它本身可能是另一個變體

[英]Get the value of a variant, which itself could be another variant

我有一個變體ScalarVar

using ScalarVar = std::variant<int, std::string>;

和變體Var ,它本身可能是一個ScalarVarstd::vectorScalarVar小號

using Var = std::variant<ScalarVar, std::vector<ScalarVar>>;

我想做一個函數template<typename T, typename Variant> T Get(const Variant& var); 當給定一個不包含內部變體的變體時,它的行為就像std::get<T> ,即如果Variant當前包含一個T ,它將返回T的值,或者如果給定一個包含其他變體的變體,它將返回遞歸獲取包含的類型,直到找到非變體,然后返回。

到目前為止,這是我最好的嘗試:

#include <iostream>
#include <variant>
#include <string>
#include <typeindex>
#include <vector>
#include <map>

template<typename T, typename... T2>
struct is_variant { static inline constexpr bool value = false; };

template<typename... T>
struct is_variant<std::variant<T...>> { static inline constexpr bool value = true; };

template<typename T, typename Variant>
T Get(const Variant& var) {
    static_assert (is_variant<Variant>::value == true, "Template parameter Variant must be a std::variant");
    auto inner = std::visit([](const auto& i){ return i; }, var);
    if constexpr(is_variant<typeof(inner)>::value) {
        return Get<T>(inner);
    }
    else return inner;
}

int main()
{
    using ScalarVar = std::variant<int, std::string>;
    using Var = std::variant<ScalarVar, std::vector<ScalarVar>>;

    ScalarVar s = 5;
    std::cout << Get<int>(s) << std::endl;

    return 0;
}

這應該簡單地返回一個T如果T不是std::variant ,並返回std::get<InnerT>如果T是的std ::變體包含類型T

但是我從 gcc 得到了一個非常復雜的編譯錯誤:

std::cout << Get<int>(s) << std::endl

/usr/include/c++/9/variant:1005: error: invalid conversion from ‘std::__success_type<std::__cxx11::basic_string<char> >::type (*)(Get(const Variant&) [with T = int; Variant = std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >]::<lambda(const auto:22&)>&&, const std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)’ {aka ‘std::__cxx11::basic_string<char> (*)(Get(const Variant&) [with T = int; Variant = std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >]::<lambda(const auto:22&)>&&, const std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)’} to ‘int (*)(Get(const Variant&) [with T = int; Variant = std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >]::<lambda(const auto:22&)>&&, const std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)’ [-fpermissive]
 1005 |       { return _Array_type{&__visit_invoke}; }
      |                                           ^
      |                                           |
      |                                           std::__success_type<std::__cxx11::basic_string<char> >::type (*)(Get(const Variant&) [with T = int; Variant = std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >]::<lambda(const auto:22&)>&&, const std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&) {aka std::__cxx11::basic_string<char> (*)(Get(const Variant&) [with T = int; Variant = std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >]::<lambda(const auto:22&)>&&, const std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)}

我怎樣才能得到我需要的行為?

問題是這一行:

std::visit([](const auto& i){ return i; }, var);

使用ScalarVar var = 5;直接從main調用它ScalarVar var = 5; 產生類似的消息。

讓我們看看cppreference關於std::visit (C++17) 的返回類型是怎么說的:

返回類型是從返回的表達式中推導出來的,就像 decltype 一樣。 對於所有變體的替代類型的所有組合,如果上述調用不是相同類型和值類別的有效表達式,則調用格式錯誤。

這意味着你不能像上面那樣調用std::visit ,因為它必須根據里面的值返回不同的類型( int / std::string )。

您可以使用函數重載並為std::variant和其他類型使用兩個不同的函數:

template <typename T, typename U>
T Get(U const& value)
{
    if constexpr (std::is_same_v<T, U>)
    {
        return value;
    }
    // If you get here, none of the nested variants had a value of type T.
    return T();
}

template <typename T, typename... Args>
T Get(std::variant<Args...> const& var)
{
    return std::visit(
        [](auto const& value) { return Get<T>(value); },
        var
    );
}

當您調用std::visit時,您需要區分Tstd::variant<...>和其他所有內容。

template<typename T>
struct Getter {
    T operator()(const T & t) { return t; }

    template <typename ... Ts>
    T operator()(const std::variant<Ts...> & var) { return std::visit(*this, var); }

    template <typename U>
    T operator()(U) { throw std::bad_variant_access(); }
};

template<typename T, typename Variant>
T Get(const Variant& var) {
    Getter<T> getter;
    return getter(var);
}

/* or 
template <typename T>
inline Getter<T> Get;
*/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM