默认从索引构造std :: variant

问题描述 投票:0回答:1

当仅在运行时知道索引时,从所需类型的索引默认构造std :: variant的最简单方法是什么?换句话说,我想写:

const auto indx = std::variant<types...>{someobject}.index();
//...somewhere later, indx having been passed around...
std::variant<types...> var = variant_from_index(indx);
///var is now set to contain a default constructed someobject

请注意,无法将indx设为constexpr,因此std::in_place_index在这里不起作用。

这里的问题当然是,由于尚不知道在编译时要从types...调用哪个构造函数,因此基本上必须以某种方式构建所有可能的构造函数的表(或从中复制默认构造变量)编译时,然后在运行时访问。这里显然有一些模板魔术,但是最干净的方法是什么?

我尝试了以下操作[on coliru,但是索引序列似乎出了错(最后的打印给出了2 0 0),我对原因感到困惑:

Edit:它的工作原理如下,我的constexpr数组初始化错误。所以现在的问题是,有没有更整洁的方法来做到这一点?

#include <variant>
#include <iostream>

using var_t = std::variant<int, float, const char *>;

//For debug
template<class ...types>
struct WhichType;

template<class T, class U>
struct default_variants;
template<class...Params, std::size_t... I>
struct default_variants<std::variant<Params...>, std::index_sequence<I...>> {
    using variant_t = std::variant<Params...>;
    //Uncomment to see the index sequence
    //WhichType<std::index_sequence<I...>> idx{};
    constexpr static variant_t variants[sizeof...(Params)]{variant_t{std::in_place_index<I>}...};
    constexpr static std::size_t indices[sizeof...(Params)]{I...};
};
template<class T>
struct default_variants_builder;
template<class...Params>
struct default_variants_builder<std::variant<Params...>> {
    using indices = std::make_index_sequence<sizeof...(Params)>;
    using type = default_variants<std::variant<Params...>, indices>;
};


int main() {
    using builder_t = typename default_variants_builder<var_t>::type;
    var_t floatvar{1.2f};
    var_t variant2 = builder_t::variants[floatvar.index()];
    std::cout << "Contained " << floatvar.index() << "; Now contains " << variant2.index() << "\n";
}
c++ templates c++17 template-meta-programming variant
1个回答
0
投票

不确定这是否非常优雅,但我认为它可行:

#include <variant>
#include <iostream>

template<typename V, std::size_t N = std::variant_size_v<V>>
struct variant_by_index {
    V make_default(std::size_t i) {
        if (i >= std::variant_size_v<V>) {
            throw std::invalid_argument("bad type index.");
        }
        constexpr size_t index = std::variant_size_v<V> - N;
        if (i == index) {
            return std::variant_alternative_t<index, V>();
        } else {
            return variant_by_index<V, N - 1>().make_default(i);
        }
    }
};

template<typename V>
struct variant_by_index<V, 0> {
    V make_default(std::size_t i) {
        throw std::invalid_argument("bad type index.");
    }
};

using var_t = std::variant<int, float, const char *>;

int main() {
    variant_by_index<var_t> type_indexer;
    var_t my_var_0 = type_indexer.make_default(0);
    std::cout << "my_var_0 has int? " << std::holds_alternative<int>(my_var_0) <<  "\n";
    var_t my_var_1 = type_indexer.make_default(1);
    std::cout << "my_var_1 has float? " << std::holds_alternative<float>(my_var_1) <<  "\n";
    try {
        var_t my_var_1 = type_indexer.make_default(3);
    } catch(const std::invalid_argument&) {
       std::cout << "Could not create with type 3.\n";
    }
    return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.