【问题标题】:Populate std::array with non-default-constructible type (no variadic templates)使用非默认可构造类型填充 std::array(无可变参数模板)
【发布时间】:2016-06-18 12:04:40
【问题描述】:

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

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

我想创建一个std::arrayA。我可以使用初始化列表轻松实现:

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 次。我想出的唯一解决方案是使用可变参数模板将参数包解压缩到初始化列表中:https://ideone.com/yEWZVq(它还检查所有副本是否被正确省略)。此解决方案的灵感来自this question

它有效,但我不想滥用可变参数模板来完成这样一个简单的任务。你知道,膨胀的可执行文件大小,减慢编译速度,所有这些。我想做这样的事情:

  1. 创建一些大小合适的未初始化存储
  2. 使用位置new 初始化循环中的所有元素
  3. 神奇地将存储转换为std::array并返回

我可以做一些肮脏的黑客来在动态内存中实现它:https://ideone.com/tbw5lm 但这并不比std::vector 好,我根本没有这样的问题。

而且我不知道如何在自动记忆中做到这一点。 IE。拥有相同的便捷函数,通过值返回 std::array,所有这些东西都在幕后。有什么想法吗?

我想,boost::container::static_vector 对我来说可能是一个很好的解决方案。不幸的是,我不能将boost 用于该特定任务。

PS。请注意,这个问题更像是研究兴趣。在现实世界中,可变参数模板和std::vector 都可以正常工作。我只是想知道我可能遗漏了什么。

【问题讨论】:

  • “膨胀的可执行文件大小”你确定吗?
  • 如果我在很多地方都这样做,我需要为每个这样的初始化添加额外的函数。不是关键项目,但仍然。这个问题更像是研究兴趣。
  • 正确使用constexpr 生成器不会影响可执行文件的大小。至于减慢编译速度,欢迎使用 C++。
  • @SamVarshavchik 你能详细说明一下吗?或者提供demo?我不确定“constexpr 生成器”是什么意思。

标签: c++ initialization c++14 stdarray


【解决方案1】:

我认为,您对代码膨胀的担忧被误解了。这是一个示例:

#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

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

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

【讨论】:

  • 很好的见解,谢谢!但是关于你的最后一点,这并不完全正确。我在描述中放置了一个链接,说明如何使用放置新的位置来做到这一点。
  • @Mikhail 似乎您决心使用新的展示位置。在这种情况下,您可能需要研究 std::aligned_storage 否则无论您设计的任何本土解决方案在您在非英特尔的东西上运行时都有可能崩溃。
  • @RichardHodges “你决定使用新的展示位置” - 我不是。我在这里提到它只是为了表明“没有其他符合要求的解决方案”的说法是错误的。所以可能还有其他一些解决方案。
  • @Mikhail 除非你的编译器超过 10 年,否则我真的不会担心代码膨胀。 Sergey 的解决方案(几乎)是完美的。
  • @RichardHodges 我同意。正如我在问题中所说,这更像是一种研究兴趣。
【解决方案2】:

这是另一种允许生成器输入任意范围的方法:

这是用例:

/// 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

即什么都没有。

【讨论】:

    猜你喜欢
    • 2018-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    相关资源
    最近更新 更多