带有非默认构造类型的填充性std ::数组(无变量模板)



假设我有一个类型A,没有默认构造函数:

struct A
{
  int x;
  A(int x) : x(x) {}
};

我想制作Astd::array。我可以轻松地使用初始化列表:

std::array<A, 5> arr = { 0, 1, 4, 9, 16 };

您可以在这里看到一个模式。是的,我可以具有一个发电机函数来计算数组的每个值:

int makeElement(size_t i)
{
  return i * i;
}
std::array<A, 5> arr = { 
  makeElement(0), 
  makeElement(1),
  makeElement(2),
  makeElement(3),
  makeElement(4)
};

是的,实际上我有5个以上的要素(64,即)。因此,不要重复makeElement 64次,这是很高兴的。我想到的唯一解决方案是使用variadic模板将参数包解放到初始化器列表中:https://ideone.com/yewzvq(它还检查所有副本是否正确含量)。该解决方案的灵感来自此问题。

它有效,但我不想滥用variadic模板执行如此简单的任务。您知道,可执行的大小,放慢了汇编,所有这些内容。我想做这样的事情:

  1. 创建一些具有适当尺寸的非初始化存储
  2. 使用位置new
  3. 初始化循环中的所有元素
  4. 神奇地将存储转换为std::array并将其返回

我可以执行一些肮脏的hack来在动态内存中实现此功能:https://ideone.com/tbw5lm,但这并不比std::vector更好,而我根本没有此类问题。

,我不知道如何在自动内存中做到这一点。IE。要具有相同方便的功能,可以通过值返回std::array,所有这些东西都在引擎盖后面。有什么想法吗?

我想,boost::container::static_vector可能是我的好解决方案。不幸的是,我无法将boost用于该特定任务。

ps。请注意,这个问题更像是研究兴趣。在现实世界中,这两个变异模板和std::vector都可以正常工作。我只想知道也许我缺少一些东西。

我想,您对代码膨胀的担忧被误解了。这是一个样本:

#include <utility>
#include <array>
template<std::size_t... ix>
constexpr auto generate(std::index_sequence<ix...> ) {
    return std::array<int, sizeof...(ix)>{(ix * ix)...};
}
std::array<int, 3> check() {
 return generate(std::make_index_sequence<3>());
}
std::array<int, 5> glob = generate(std::make_index_sequence<5>());

它产生非常整洁的组装:

check():
        movl    $0, -24(%rsp)
        movl    $1, -20(%rsp)
        movl    $4, %edx
        movq    -24(%rsp), %rax
        ret
glob:
        .long   0
        .long   1
        .long   4
        .long   9
        .long   16

如您所见,看不到代码。静态数组是静态初始化的,对于自动阵列,它只是一堆动作。而且,如果您认为一堆动作是一个可怕的代码膨胀,请考虑循环展开 - 每个人都喜欢!

顺便说一句,没有其他符合解决方案。数组是在施工时使用聚合初始化初始化的,因此元素应默认构造或初始化。

这是允许发电机的任意输入范围的另一种方法:

这是用例:

/// generate an integer by multiplying the input by 2
/// this could just as easily be a lambda or function object
constexpr int my_generator(int x) {
    return 2 * x;
}
int main()
{
    // generate a std::array<int, 64> containing the values
    // 0 - 126 inclusive (the 64 acts like an end() iterator)
    static constexpr auto arr = generate_array(range<int, 0, 64>(),
                                               my_generator);
    std::copy(arr.begin(), arr.end(), std::ostream_iterator<int>(std::cout, ", "));
    std::cout << std::endl;
}

这是允许其工作的样板

#include <utility>
#include <array>
#include <iostream>
#include <algorithm>
#include <iterator>
/// the concept of a class that holds a range of something
/// @requires T + 1 results in the next T
/// @requires Begin + 1 + 1 + 1.... eventually results in Tn == End
template<class T, T Begin, T End>
struct range
{
    constexpr T begin() const { return Begin; }
    constexpr T end() const { return End; }
    constexpr T size() const { return end() - begin(); }
    using type = T;
};
/// offset every integer in an integer sequence by a value
/// e.g offset(2, <1, 2, 3>) -> <3, 4, 5>
template<int Offset, int...Is>
constexpr auto offset(std::integer_sequence<int, Is...>)
{
    return std::integer_sequence<int, (Is + Offset)...>();
}
/// generate a std::array by calling Gen(I) for every I in Is
template<class T, class I, I...Is, class Gen>
constexpr auto generate_array(std::integer_sequence<I, Is...>, Gen gen)
{
    return std::array<T, sizeof...(Is)> {
        gen(Is)...
    };
}
/// generate a std::array by calling Gen (x) for every x in Range
template<class Range, class Gen>
constexpr auto generate_array(Range range, Gen&& gen)
{
    using T = decltype(gen(range.begin()));
    auto from_zero = std::make_integer_sequence<typename Range::type, range.size()>();
    auto indexes = offset<range.begin()>(from_zero);
    return generate_array<T>(indexes, std::forward<Gen>(gen));
}
/// generate an integer by multiplying the input by 2
constexpr int my_generator(int x) {
    return 2 * x;
}
int main()
{
    static constexpr auto arr = generate_array(range<int, 0, 64>(),
                                               my_generator);
    std::copy(arr.begin(), arr.end(), std::ostream_iterator<int>(std::cout, ", "));
    std::cout << std::endl;
}

这是汇编之前查看的代码膨胀:

.LC0:
    .string ", "
main:
;; this is the start of the code that deals with the array
    pushq   %rbx
    movl    main::arr, %ebx
.L2:
    movl    (%rbx), %esi
    movl    std::cout, %edi
    addq    $4, %rbx
;; this is the end of it
;; all the rest of this stuff is to do with streaming values to cout
    call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
    movl    $2, %edx
    movl    $.LC0, %esi
    movl    std::cout, %edi
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    cmpq    main::arr+256, %rbx
    jne     .L2
    movl    std::cout, %edi
    call    std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
    xorl    %eax, %eax
    popq    %rbx
    ret
    subq    $8, %rsp
    movl    std::__ioinit, %edi
    call    std::ios_base::Init::Init()
    movl    $__dso_handle, %edx
    movl    std::__ioinit, %esi
    movl    std::ios_base::Init::~Init(), %edi
    addq    $8, %rsp
    jmp     __cxa_atexit
main::arr:
    .long   0
    .long   2
    .long   4
    .long   6
    .long   8
    .long   10
    .long   12
    .long   14
    .long   16
    .long   18
    .long   20
    .long   22
    .long   24
    .long   26
    .long   28
    .long   30
    .long   32
    .long   34
    .long   36
    .long   38
    .long   40
    .long   42
    .long   44
    .long   46
    .long   48
    .long   50
    .long   52
    .long   54
    .long   56
    .long   58
    .long   60
    .long   62
    .long   64
    .long   66
    .long   68
    .long   70
    .long   72
    .long   74
    .long   76
    .long   78
    .long   80
    .long   82
    .long   84
    .long   86
    .long   88
    .long   90
    .long   92
    .long   94
    .long   96
    .long   98
    .long   100
    .long   102
    .long   104
    .long   106
    .long   108
    .long   110
    .long   112
    .long   114
    .long   116
    .long   118
    .long   120
    .long   122
    .long   124
    .long   126

即。无。

最新更新