【问题标题】:using std::index_sequence to initialize POD struct container with fixed size array members使用 std::index_sequence 初始化具有固定大小数组成员的 POD 结构容器
【发布时间】:2020-06-30 21:02:50
【问题描述】:

我正在尝试使用 std::integer_sequence 及其帮助模板 std::index_sequence 来初始化一个固定大小的 POD 结构,它的行为类似于一个容器。每个元素又包含一个固定大小的数组字段。

容器 POD 结构体本质上定义为:

#define ELEM_NAME_SIZE   7
#define CONTAINER_SIZE  20

using Container = struct Container {
    int entries;
    Element elems[MAX_CONTAINER_SIZE];    
};

各个元素是:

using Element = struct Element {
    char name[MAX_NAME_CHARS];
    bool bFlag;
};

基于question 的答案,我能够使用std::index_sequence 来初始化固定长度Elements。到目前为止一切顺利。

我需要想出一种方法来构造 Container 使用单个固定大小的 const char* - 比如说“ABCDEF”,或者使用固定长度的数组(最大为 ELEM_NAME_SIZE)字符串.

constexpr char gTestNames[3][ELEM_NAME_SIZE] = {
    "APPLE", "BEE", "CHAIN"
};

live coliru code中,这些容器构造函数中的第一个如下:

template<std::size_t N, typename Indices = std::make_index_sequence<N>>
constexpr Container makeContainer(char const (&name)[N]) {
    return makeContainer_in(name, Indices{});
}

用这个产量构造并打印出容器的内容:

const auto container = makeContainer("ABCDEF");
std::cout << container << '\n';

输出:

Container: entries(7)[Element:[ABCDEF],,Element:[],,Element:[],,Element:[],,Element:[],,Element:[],,Element:[],,]

但是,使用替代模板函数重载,带有参数gTestNames

template<std::size_t N, std::size_t NAME_LEN_MAX = ELEM_NAME_SIZE, typename Indices = std::make_index_sequence<N>>
constexpr Container makeContainer(const char(&names)[N][NAME_LEN_MAX]) {
    return makeContainer_in(names, Indices{});
}

调用使用:

const auto container1 = makeContainer(gTestNames);
std::cout << container1 << '\n';;

我得到以下错误输出:

main.cpp: In instantiation of 'constexpr Container makeContainer(const char (&)[N][NAME_LEN_MAX]) [with long unsigned int N = 3; long unsigned int NAME_LEN_MAX = 7; Indices = std::integer_sequence<long unsigned int, 0, 1, 2>; Container = Container]':
main.cpp:78:53:   required from here
main.cpp:64:28: error: no matching function for call to 'makeContainer_in(const char [3][7], std::integer_sequence<long unsigned int, 0, 1, 2>)'
   64 |     return makeContainer_in(names, Indices{});
      |            ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
main.cpp:52:21: note: candidate: 'template<long unsigned int N, long unsigned int ...Is> constexpr Container makeContainer_in(const char (&)[N], std::index_sequence<Is ...>)'
   52 | constexpr Container makeContainer_in(char const (&packed)[N], std::index_sequence<Is...>) {
      |                     ^~~~~~~~~~~~~~~~
main.cpp:52:21: note:   template argument deduction/substitution failed:
main.cpp:64:28: note:   mismatched types 'const char' and 'const char [7]'
   64 |     return makeContainer_in(names, Indices{});
      |            ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~

如果可能的话,除了纠正我的错误之外,还有什么方法可以简化所有这些样板代码 - 也许使用折叠表达式 - 我想使用 index_sequences(因为我正在尝试学习如何使用它们),但我从来不理解总是将调用转发到某种代理以扩展索引的要求。一定有更简单的方法。

【问题讨论】:

  • using Var = struct Var {}; 的意义何在?为什么不只是struct Var
  • @KamilCuk 在我有诊断朋友打印机的地方查看完整的 coliru - 这就是为什么我再次表示“基本上”你好我的朋友 :) - 正如你所看到的,我正在努力从你的指导中学习跨度>
  • using Container = struct Container { 请不要这样做。你在哪里看到的?
  • 我需要一个朋友 fn 的前向参考,该参考显示在 coliru 项目中,而不是在问题中
  • @johnco3: template-head 是看起来像template&lt;…&gt; 的句法组件;因为你不能从默认参数中推断出来,所以调用者必须明确地构造了一个std::integer_sequence,这使得调用者成为符合人体工程学的包装器。

标签: c++ c++17 variadic-templates variadic-functions


【解决方案1】:

如果你想要在给定HELLO的输出之后输出

 Element:[H], Element:[E], Element:[L], Element:[L], Element:[O],

此代码将起作用,尽管如果您传递一个以空字符结尾的字符串,则会有一个空字符元素。

#include <iostream>
#include <tuple>

#define MAX_NAME_CHARS 9
#define MAX_CONTAINER_SIZE 100

struct Element {
    char name[MAX_NAME_CHARS];
    bool bFlag;
    int foo;
    friend std::ostream& operator << (std::ostream& os, const Element& next) {
        os << next.name;
        return os;
    }    
};

struct Container {
    int entries;
    Element elems[MAX_CONTAINER_SIZE];    
    friend std::ostream& operator << (std::ostream& os, const Container& next) {        
            os << "Container: entries(" << next.entries << ")[";
            for (auto i = 0; i<next.entries; ++i) {
                os << next.elems[i] << ",";
            }
            os << "]\n";
            return os;
    } 
};

template<std::size_t N, std::size_t ... I>
constexpr Container makeContainerInSingle(const char(&singlecharnames)[N],
                                 std::index_sequence<I...>) {
    auto result = Container {
        N,
        {Element{
            {singlecharnames[I]},
            true, 
            0}...
        }
    };
    return result;
}

template<std::size_t N>
constexpr Container makeContainerSingle(const char(&singlecharnames)[N]) {
    return makeContainerInSingle(singlecharnames, std::make_index_sequence<N>{});
}

int main() {
    auto c2 = makeContainerSingle("HELLO");
    std::cout << c2 << std::endl;
}

输出如下

Container: entries(6)[H,E,L,L,O,,]

作为附加说明,我不确定可以使用std::index_sequence 来简化模式,尽管如果您认为索引序列更简单,可以像我的示例代码中那样直接构造索引序列。

【讨论】:

  • @UnforseenPrincess 谢谢,当我意识到我没有正确扩展元素而是扩展了元素的名称字段时,我实际上想通了http://coliru.stacked-crooked.com/a/c94182b9ad3d73be。我还意识到我必须将 template&lt;std::size_t N, std::size_t... Is&gt; constexpr Container makeContainer_in(char const (&amp;packed)[N], std::index_sequence&lt;Is...&gt;) { return Container{ N-1, {{ packed[Is], false }...} }; } N 调整为 N-1 因为 N 包括初始 const char* 的终止零
  • @UnforseenPrincess +1 为std::make_index_sequence&lt;N&gt;{} 节省了相当多的样板代码,并在我看来使其更具可读性。谢谢 - 很好的答案
【解决方案2】:

当然:

template<std::size_t N, std::size_t... Is>
constexpr Container makeContainer_in(char const (&packed)[N], std::index_sequence<Is...>) {
     return Container{ N, { packed[Is]... } };
}

不起作用,Container 有一个Element,而不是chars(并且Element 没有Element::Element(char) 构造函数)。只需使用参数包中的构造函数实例化实际的容器元素即可。

template<std::size_t N, std::size_t... Is>
constexpr Element makeElement_in(char const (&name)[N], std::index_sequence<Is...>) {
    return Element{ { name[Is]... }, true, };
}

template<std::size_t N, typename Indices = std::make_index_sequence<N>>
constexpr Element makeElement(char const (&name)[N]) {
    return makeElement_in(name, Indices{});
}

template<std::size_t N, std::size_t NAME_LEN_MAX, std::size_t... Is>
constexpr Container makeContainer_in(const char(&names)[N][NAME_LEN_MAX], std::index_sequence<Is...>) {
    return Container{ N, { makeElement(names[Is])... } };
}

template<std::size_t N, std::size_t NAME_LEN_MAX, typename Indices = std::make_index_sequence<N>>
constexpr Container makeContainer(const char(&names)[N][NAME_LEN_MAX]) {
    return makeContainer_in(names, Indices{});
}

Godbolt link.

【讨论】:

  • 与 makeContainer_in 调整以包含缺少的标志参数模板函数编译,即 template&lt;std::size_t N, std::size_t... Is&gt; constexpr Container makeContainer_in(char const (&amp;packed)[N], std::index_sequence&lt;Is...&gt;) { return Container{ N, { packed[Is]..., false } }; } 但是结果并不完全符合我的预期,因为我得到了 Container: entries(6)[Element:[HELLO],, Element:[],,Element:[],,Element:[],,Element:[],,Element:[],,] 我试图创建 Element:[H], Element:[E], Element :[L], Element:[L], Element:[O], 看起来我需要在 Element 外扩展包
  • 啊哈!!!! template&lt;std::size_t N, std::size_t... Is&gt; constexpr Container makeContainer_in(char const (&amp;packed)[N], std::index_sequence&lt;Is...&gt;) { return Container{ N, {{ packed[Is], false }...} }; } 给出:Container: entries(6)[Element:[H],,Element:[E],,Element:[L],,Element:[L],,Element:[O],,Element:[],,] 注意元素周围的额外大括号,以确保整个元素都发生扩展,而不是为第一个元素扩展 HELLO
猜你喜欢
  • 2019-01-11
  • 2018-10-24
  • 2020-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-20
  • 2015-11-28
  • 1970-01-01
相关资源
最近更新 更多