【问题标题】:Eloquent solution to needing non-const indexed access of a std::tuple (i.e. std::get<E>)需要对 std::tuple 进行非常量索引访问的雄辩解决方案(即 std::get<E>)
【发布时间】:2016-07-12 03:49:42
【问题描述】:

tl;dr 是否有任何简单的方法可以将std::get 与非常量索引一起使用?


我知道这个问题已经被问过很多次了,但所提出的解决方案似乎都不是特别简单、雄辩或适合我的情况。

我正在寻找的是:一种使用std::get&lt;E&gt; 的无痛方式,其中E 是任何表达式,以便将T&amp; 返回到已知存在的T(即不受范围异常影响,因此在std::tuple&lt;T&gt; 中绕过安全范围std::get)。

问题似乎是编译器需要知道std::getreturn 的类型,但我将手动输入数据及其类型,不需要确定类型。 (也许auto 可以在某处使用?)

我想这样做的原因可以通过下面的sn-ps来解释。我正在尝试创建一个数据容器类,您可以使用初始化列表来填充它。


  1. Point:用于允许元组的模拟初始化器列表。

    template<class... T>
    class Point {
    public:
        std::tuple<T...> get() const { return data; }
        Point(T... t) {
            data = std::tuple<T...>(t...);
        }
    private:
        std::tuple<T...> data;
    };
    
  2. 数据:设计用于在std::tuplestd::vector&lt;T&gt; 中保存动态数据量,其中T 是一组用户定义类型中的每一个。

    template<typename... T>
    class Data {
    public:
        Data(std::initializer_list<Point<T...>> data = {{}})
            : columns(sizeof...(T))
        {
            // ???
        }
    private:
        byte columns;
        std::tuple<std::vector<T>...> datas;
    };
    
  3. 结合起来,这应该允许这种简单的格式用于快速数据输入(或未来来自外部源的数据流):

    int main() {
        Data<char, int, float> {
            { 'A', 1, 3.14 } ,
            { 'B', 2, 6.28 }
        };
    
        return 0;
    }
    

这些旨在稍后被移植到一个充满有用扩展的库中,这些扩展以使非程序员可以轻松输入数据和公式而无需大惊小怪的方式处理数据。公式的一半是有效的,但是由于std::get的限制,我仍然遇到了这一半的问题:

for (auto d : data) {
    for (int i = 0; i < columns; ++i) {
        std::get<i>(datas).push_back(std::get<i>(data));
        // doesn't work; std::get<N> requires const N to know return type to use push_back
    }
}

我觉得这真的很令人沮丧,就像大多数人一样。我不能用std::arrays 或std::vectors 替换datas,因为有多种类型;尽管boost::any,我还是想避免使用boostswitch (column) { case 0: std::get&lt;0&gt;(...)... } 可用于模仿动态 std::get,但如果我不能为每个可能的第 n 列手动输入一个案例;仅仅为了这个小目的而对一种新的容器类型进行元编程似乎过于复杂了;并且似乎没有其他快速修复解决方案足以应对这些特殊情况。

【问题讨论】:

  • 如果 E 直到运行时才知道,那么您希望编译器如何确定 get&lt;E&gt;(arg) 的类型?
  • 顺便说一句,C++17 中有std::any
  • 为什么要将columns 存储为运行时值?它是sizeof...(T)Data&lt;T...&gt;“知道”。如果您想要“针对元组的每个元素”,甚至“针对元组的每个索引”,这需要运行时get。据我所知,如果你有一个运行时get&lt;I&gt;,你认为你可以解决一个模糊指定的实际问题。这被称为 XY 问题。您能否完善您的问题以专注于您的实际问题,并包括您尝试的解决方案以及出了什么问题作为细节不是重点?
  • @Yakk 添加了更多具体信息。如果不存储sizeof... (T),我如何计算每个push_back 数据中有多少种类型std::vector
  • @carson 类型 Datas 和元组都知道它们有多宽。单独存储值没有帮助。

标签: c++ tuples variadic


【解决方案1】:

tl;dr 有什么简单的方法可以将 std::get 与非常量索引一起使用吗?

不,因为根本没有一种方法可以将它与非常量索引一起使用。既不难也不好用。

但是你可以让你的构造函数像

Data(std::initializer_list<Point<T...>> data)
{
  for (auto&& d : data)
  {
    tup_to_vectup(std::make_index_sequence<sizeof...(T)>{},
      datas, d.data);
  }
}

有一个像这样的小助手

template<std::size_t ... I, class VecTup, class Tup>
void tup_to_vectup(std::index_sequence<I...>, VecTup&& vt, Tup&& t)
{
  int a[] = {
    0, (std::get<I>(vt).push_back(std::get<I>(t)), 0)...
  };
  (void)a;
}

因为您实际上不需要非常量索引。您的类模板参数...T 在编译时确定索引。您只需要将它们转换为索引序列并使用它来迭代您的元组。

【讨论】:

  • 非常感谢!那就是我断开连接的地方-我不知道std::make_index_sequence,这是整个问题的关键。我还需要阅读 rvalue refs...
【解决方案2】:
template<std::size_t I>
using index_t=std::integral_constant<std::size_t,I>;

template<std::size_t...Is, class F>
void for_each_from_indexes( std::index_sequence<Is...>, F&& f ){
  using discard=int[];
  (void)discard{0,(void(
    f(index_t<Is>{})
  ),0)...};
}
template<std::size_t N, class F>
void for_each_index( F&& f ){
  for_each_from_indexes( std::make_index_sequence<N>{}, std::forward<F>(f) );
}

现在我们可以在编译时使用一组索引调用 lambda。

template<class...Ts>
void add_data(std::tuple<Ts...> const& data){
  for_each_index<sizeof...(Ts)>([&](auto i){
    std::get<i>(datas).push_back(std::get<i>(data));
  });
}

i这里转为index不是运行时,而是编译时。

需要 C++14,但它是 2016。

可能有错别字。

【讨论】:

  • 我选择了 Pixelchemist 的答案,但你的答案肯定会获得荣誉奖,我绝对可以用这个 sn-p 工作。非常感谢!
  • @CarsonWilber:这两种方法在某种意义上实际上是相关的/相似的,但是这个解决方案比我的更通用,并且就编写库的意图而言是一个更好的选择。
  • 实际上,VS15 似乎仍然缺乏对此的支持 (std::get...expected compile-time constant expression。) 我得想点别的办法。
  • @carson decltype(i)::valuedecltype(i){} 而不是 i
  • 我的立场是正确的。有趣的!为什么需要 decltype?
猜你喜欢
  • 1970-01-01
  • 2014-01-30
  • 1970-01-01
  • 2020-07-07
  • 2021-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多