【问题标题】:can I initialize an std::tuple from a vector array?我可以从向量数组初始化 std::tuple 吗?
【发布时间】:2020-03-20 15:46:56
【问题描述】:

我有一个包含变体类的 std::vector。我想用相同的数据构造一个元组。这可能吗?元组的正常构造方法似乎很受限制。

                    //In reality, I'm using JUCE::var.
                    // SimpleVariant is here just to make the example code more explicit.
struct SimpleVariant
{
    SimpleVariant(int i) :                a(i), b("") {}
    SimpleVariant(const std::string& s) : a(0), b(s) {}

    operator int() const { return a; }
    operator std::string() const { return b; }

private:
    int a;
    std::string b;
};


template <typename... T>
struct VariantTuple
{
    VariantTuple(const std::vector<SimpleVariant>& v)
    {
        // how do I initialize the tuple here?
    }

private:
    std::tuple<T...> tuple;
};


        std::vector<SimpleVariant> v{ SimpleVariant(1),
                                      SimpleVariant(2),
                                      SimpleVariant("a") };

        VariantTuple<int, int, std::string> t (v);

基于 cmets 的一些说明:

我不需要元组来逐项匹配数组,或者从给定数组中推断出类型。我想取一个给定的数组,然后提取与某种类型匹配的变体。因此,例如,给定上述数组v,我希望能够构造一个VariantTuple&lt;int, std::string&gt;,并让它匹配术语“1”和“a”。这引入了许多其他问题,超出了我原来问题的范围。但是我现在感兴趣的问题是,是否有可能首先基于数组构造一个元组。

【问题讨论】:

  • 题名提到了数组,但题测试和示例使用vectors。你问的是哪个?
  • 如果将结果元组作为函数参数而不是直接返回值给出,这将是可能的,但它需要编译所有可能的类型排列,这很快就会在变量大小和向量长度上爆炸.
  • @chris 您需要设置向量大小的上限以编译每个可能生成的元组。这不是很实用。不过你可以用std::array 做到这一点。
  • @FrançoisAndrieux,对,它对于低长度是可行的,而且编译时间对于更高的长度来说真的很糟糕。
  • 您实际上是在使用SimpleVariant 还是只是一个例子?你可以改用std::variant 吗? SimpleVariant 看起来非常难以用作 variant,并且会使您尝试使用它做的任何事情变得更加复杂。

标签: c++ tuples stdtuple


【解决方案1】:

好吧,我不确定您是否要求动态推断向量元素的数量并构造元组,这是不可能的,但是您可以。我使用std::index_sequence 来推断元组元素的数量,具体取决于VariantTuple 的参数大小。这需要 C++17,因为它使用折叠表达式。

#include <initializer_list>
#include <string>
#include <vector>
#include <tuple>
#include <utility>
#include <type_traits>
#include <ostream>
#include <iostream>

struct SimpleVariant
{
    SimpleVariant(int i) :                a(i), b("") {}
    SimpleVariant(const std::string& s) : a(0), b(s) {}

    operator int() const {
        return a;
    }

    operator std::string() const {
        return b;
    }

    int a;
    std::string b;
};

template<typename V, size_t... dim, typename... Args>
auto populate_tuple(const V& vec, std::index_sequence<dim...>, const std::tuple<Args...>& t) {
    return std::make_tuple(static_cast<std::remove_reference_t<decltype(std::get<dim>(t))>>(vec.at(dim))...);
}

template<size_t... dim, typename... Args>
std::ostream& dump_tuple(std::ostream& out, const std::tuple<Args...>& tpl, std::index_sequence<dim...>) {
    ((out << std::get<dim>(tpl) << ","), ...);
    return out;
}

template<typename... T>
struct VariantTuple
{
    VariantTuple(const std::vector<SimpleVariant>& v) : tpl(populate_tuple(v, std::make_index_sequence<sizeof...(T)>{}, tpl)) {}

    template<typename... V>
    friend std::ostream& operator <<(std::ostream& out, const VariantTuple<V...>& vt) {
        return dump_tuple(out, vt.tpl, std::make_index_sequence<sizeof...(V)>{});
    }
private:
    std::tuple<T...> tpl;
};

int main() {
    std::vector<SimpleVariant> v { 
        SimpleVariant(1),
        SimpleVariant(2),
        SimpleVariant("a") 
    };
    VariantTuple<int, int, std::string> t (v);
    std::cout << t << std::endl;

    return 0;
}

【讨论】:

  • 那么您已经构建了向量,不是吗?执行 URL,wandbox.org/permlink/QL6DP5EC1tBrFZHB
  • 我已经为此使用了用户定义的转换运算符
  • populate_tuple 中将t 更改为std::tuple&lt;Args...&gt; &amp; t 为我修复了它。它正在执行未初始化的tuple 的副本。
  • @FrançoisAndrieux,您需要通过引用传递元组,我将编辑答案,gcc 没有显示警告,clang 显示了。
  • 这似乎很好,更改为t。但是将未初始化的tpl 作为参数传递给populate_tuple 是代码异味,它应该在任何代码审查中引起关注。也许您可以通过传递 decltype(tpl) 作为模板参数来传递元组,并使用 std::tuple_element_t&lt;dim, T&gt; 而不是 decltype(std::get&lt;dim&gt;(t))&gt;。编辑:示例:godbolt.org/z/9tCvc7
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-31
  • 2012-05-01
  • 1970-01-01
相关资源
最近更新 更多