简体   繁体   English

使用非默认可构造类型填充std :: array(无可变参数模板)

[英]Populate std::array with non-default-constructible type (no variadic templates)

Suppose I have a type A with no default constructor: 假设我有一个没有默认构造函数的A型:

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

I want to make an std::array of A . 我想做一个std::array A I can easily make it with initializer list: 我可以使用初始化列表轻松实现它:

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

You can see a pattern here. 您可以在此处看到一个模式。 Yes, I can have a generator function to compute each value of the array: 是的,我可以使用生成器函数来计算数组的每个值:

int makeElement(size_t i)
{
  return i * i;
}

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

And yes, in fact I have much more than 5 elements (64, namely). 是的,实际上我有5个以上的元素(即64个)。 So it would be nice not to repeat makeElement 64 times. 因此,最好不要重复makeElement 64次。 The only solution I came up with is to use variadic templates to unpack parameter pack into the initializer list: https://ideone.com/yEWZVq (it also checks that all copies are properly elided). 我想出的唯一解决方案是使用可变参数模板将参数包解包到初始化列表中: https//ideone.com/yEWZVq (它还会检查所有副本是否被正确删除)。 This solution was inspired by this question . 该解决方案受到此问题的启发。

It works, but I would like not to abuse variadic templates for such a simple task. 它可以工作,但是我不想滥用可变参数模板来完成这样一个简单的任务。 You know, bloating executable size, slowing down the compilation, all that stuff. 您知道,膨胀可执行文件的大小,减慢了编译速度,所有这些事情。 I would like to do something like this: 我想做这样的事情:

  1. Create some uninitialized storage with a proper size 创建一些适当大小的未初始化存储
  2. Initialize all elements in the loop with placement new new位置初始化循环中的所有元素
  3. Magically convert the storage to std::array and return it 神奇地将存储转换为std::array并返回

I can do some dirty hacks to implement this in dynamic memory: https://ideone.com/tbw5lm But this is not better than a std::vector , where I do not have such problems at all. 我可以做一些肮脏的技巧以在动态内存中实现此目的: https : //ideone.com/tbw5lm但这并不比std::vector ,我根本没有这类问题。

And I have no idea how I can do it in automatic memory. 而且我不知道如何在自动记忆中做到这一点。 Ie to have the same convenient function returning std::array by value with all this stuff behind the hood. 即具有相同的便捷函数,可以按值返回std::array ,而所有这些东西都在后台。 Any ideas? 有任何想法吗?

I suppose, that boost::container::static_vector might be good solution for me. 我想, boost::container::static_vector可能对我来说是一个很好的解决方案。 Unfortunately, I cannot use boost for that particular task. 不幸的是,我无法对特定任务使用boost

PS. PS。 Note please that this question is more like of research interest. 请注意,这个问题更像是研究兴趣。 In a real world both variadic templates and std::vector would work just fine. 在现实世界中,可变参数模板和std::vector都可以正常工作。 I just want to know maybe I am missing something. 我只是想知道也许我缺少了一些东西。

I think, your worries about code bloat are misconstrued. 我认为,您对代码膨胀的担心被误解了。 Here is a sample: 这是一个示例:

#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>());

It produces very neat assembly: 它产生非常整齐的组装:

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

As you see, no code bloat in sight. 如您所见,看不到代码膨胀。 Static array is statically initialized, and for automatic array it just a bunch of moves. 静态数组是静态初始化的,对于自动数组,它只是一堆动作。 And if you think bunch of moves is a dreaded code bloat, consider it loop unrolling - which everybody loves! 而且,如果您认为一堆举动是令人恐惧的代码膨胀,请考虑将其循环展开-每个人都喜欢!

By the way, there is no other conforming solution. 顺便说一句,没有其他符合性的解决方案。 Array is initialized using aggregate initialization at construction time, so elements should either be default constructible or be initialized. 数组在构造时使用聚合初始化进行初始化,因此元素应该是默认可构造的或被初始化。

Here's another way which allows an arbitrary range of inputs to the generator: 这是允许向生成器输入任意范围的另一种方法:

Here's the use case: 这是用例:

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

Here's the boilerplate to allow it to work 这是允许其工作的样板

#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;
}

here's the code bloat as viewed prior to assembly: 这是汇编之前查看的代码膨胀:

.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

ie none whatsoever. 即没有任何。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 初始化非默认可构造元素的 std::array? - Initializing an std::array of non-default-constructible elements? std::map emplace 不可移动不可复制非默认可构造类型 - std::map emplace non-movable non-copyable non-default-constructible type 如何初始化std :: array <T, 2> 其中T是不可复制的,非默认可构造的? - How to initialize an std::array<T, 2> where T is non-copyable and non-default-constructible? 如何正确初始化非默认可构造的类成员? - How to properly initialize non-default-constructible class member? 如何初始化不可默认构造的不可复制对象的元组? - How to initialize a tuple of non-default-constructible not-copyable objects? 移动具有非默认构造 class 保护的构造函数 - Move constructor with protection for non-default-constructible class 创建非默认可构造类的虚拟对象 - Create dummy object of non-default-constructible class 如何创建C ++ 11不可默认构造的分配器? - How to create a C++ 11 non-default-constructible allocator? 使用 Boost.Python 导出非默认可构造类 - Exporting non-default-constructible classes with Boost.Python 如何使用 C++ 11 样式的非默认构造分配器指定初始大小? - How to specify an initial size using C++ 11 style non-default-constructible allocator?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM