使用std :: variant的std :: variant是否可以

时间:2019-12-09 14:01:46

标签: c++ variant

我正试图将两个变体组合为一个变体,以提高可读性。这是代码:

using VariantType_basic = std::variant<int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, std::string>;
using VariantType_vector = std::variant<vector<int8_t>, vector<uint8_t>, vector<int16_t>, vector<uint16_t>, vector<int32_t>, vector<uint32_t>, vector<int64_t>, vector<uint64_t>, vector<std::string>>;
using VariantType_all = std::variant<VariantType_basic, VariantType_vector>;

class Container {
    public:
        Container(){}
        template<typename T>
        T get(string key, bool &found){
            found = false;
            T result;
            auto elem = m_internal_map.find(key);
            if(elem != m_internal_map.end())
                std::visit( 
                    [&](const auto& v){ 
                        // if(holds_alternative<T>(v)){

                        result = std::get<T>(v);
                        found = true;
                    //} 
                },  
                    elem->second 
                    );

            return result;
        }

        template<typename T>
        void put(string key, T&elem){

        }
    private:
        map<string, VariantType_all> m_internal_map;
};

当我尝试执行以下操作时,get()方法在result = std::get<T>(v);的编译时失败:

Container cont;
bool found;
cont.get<uint16_t>("www", found);

错误消息很大,但是第一条错误消息是:/usr/include/c++/7/variant:762:7: error: static assertion failed: T should occur for exactly once in alternatives

我应该停止尝试使用变体吗?

3 个答案:

答案 0 :(得分:3)

我建议“变平”变体,而不要使用变体:

template <typename Var1, typename Var2> struct variant_flat;

template <typename ... Ts1, typename ... Ts2>
struct variant_flat<std::variant<Ts1...>, std::variant<Ts2...>>
{
    using type = std::variant<Ts1..., Ts2...>;
};

using VariantType_basic = std::variant<int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, std::string>;
using VariantType_vector = std::variant<std::vector<int8_t>, std::vector<uint8_t>, std::vector<int16_t>, std::vector<uint16_t>, std::vector<int32_t>, std::vector<uint32_t>, std::vector<int64_t>, std::vector<uint64_t>, std::vector<std::string>>;
using VariantType_all = variant_flat<VariantType_basic, VariantType_vector>::type;

并且您的班级只需要处理一个变体级别:

class Container
{
public:
    Container(){}

    template<typename T>
    T get(const std::string& key, bool &found) const {
        found = false;
        T result;
        auto elem = m_internal_map.find(key);
        if (elem != m_internal_map.end() && std::holds_alternative<T>(elem->second)){
            result = std::get<T>(elem->second);
            found = true;
        }
        return result;
    }

    template<typename T>
    void put(const std::string& key, const T& elem) {
        m_internal_map[key] = elem;
    }
private:
    std::map<std::string, VariantType_all> m_internal_map;
};

Demo

答案 1 :(得分:2)

问题在于std::visit将在编译时实例化对 all 变体类型的lambda的调用,然后仅在运行时选择与变体中包含的实际值相对应的那个。通过当前编写lambda的方式,这意味着它将尝试实例化

result = std::get<T>(v);
对于既有T

,同时包含VariantType_basicVariantType_vector的lambda版本。由于您的VariantType_vector根本不包含std::uint16_t,因此第二个实例化将由于给定的错误而无法编译,因为您试图以不包含{的类型来调用std::get<T> {1}}完全在替代清单中……

解决此问题的一种方法是编写访问者lambda,以便仅针对变量值实例化调用T的代码,这些变量值实际上可能包含您要查找的类型的值:

std::get<T>

然后

template <typename T, typename V>
constexpr bool has_variant = false;

template <typename T, typename... Types>
constexpr bool has_variant<T, std::variant<Types...>> = (std::is_same_v<T, Types> || ...);

template <typename T, typename V>
constexpr bool has_variant<T, V&> = has_variant<T, V>;

说了这么多,对于我来说,整个结构似乎是一个非常脆弱的解决方案,可以用其他方法更好地解决这个问题。我建议在这里重新考虑您的方法……

答案 2 :(得分:0)

您可以使用第二级visit来嵌套嵌套变量:

template < typename T >
struct Getter
{
    T& value;
    bool& found;
    void operator()(const T& t)
    {
        value = t;
        found = true;
    }
    template < typename U >
    void operator()(const U& u) {}
};

if(elem != m_internal_map.end()) {
   Getter< T > getter = { result, found };
   std::visit( getter, elem->second );
}

如果该类型不是所需的类型,那么Getter将忽略它。请注意,Getter还将捕获可转换为T的值,因此可能不是您想要的。

使用单一级别的变体肯定会更简单。