用非默认可构造类型填充std :: array(没有可变参数模板) [英] Populate std::array with non-default-constructible type (no variadic templates)
问题描述
假设我有一个A
类型,没有默认构造函数:
Suppose I have a type A
with no default constructor:
struct A
{
int x;
A(int x) : x(x) {}
};
我想将std::array
设置为A
.我可以使用初始化列表轻松实现它:
I want to make an std::array
of 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)
};
是的,实际上我有5个以上的元素(即64个).因此,最好不要重复makeElement
64次.我想出的唯一解决方案是使用可变参数模板将参数包解包到初始化程序列表中: https://ideone.com/yEWZVq (它还会检查所有副本是否被正确删除).此解决方案的灵感来自此问题.
And yes, in fact I have much more than 5 elements (64, namely). So it would be nice not to repeat makeElement
64 times. 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). 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:
- 创建一些适当大小的未初始化存储
- 使用放置位置
new
初始化循环中的所有元素
- 神奇地将存储转换为
std::array
并返回
- Create some uninitialized storage with a proper size
- Initialize all elements in the loop with placement
new
- Magically convert the storage to
std::array
and return it
我可以做一些肮脏的技巧以在动态内存中实现此目标: https://ideone.com/tbw5lm但这并不比std::vector
好,我根本没有这样的问题.
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.
我不知道如何在自动记忆中做到这一点. IE.具有相同的便捷功能,可以按值返回std::array
,而所有这些东西都在后台.有什么想法吗?
And I have no idea how I can do it in automatic memory. I.e. to have the same convenient function returning std::array
by value with all this stuff behind the hood. Any ideas?
我想,boost::container::static_vector
对我来说可能是一个很好的解决方案.不幸的是,我不能使用boost
来完成特定任务.
I suppose, that boost::container::static_vector
might be good solution for me. Unfortunately, I cannot use boost
for that particular task.
PS.请注意,这个问题更像是研究兴趣.在现实世界中,可变参数模板和std::vector
都可以正常工作.我只是想知道也许我缺少什么.
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. I just want to know maybe I am missing something.
推荐答案
这是允许向生成器输入任意范围的另一种方法:
Here's another way which allows an arbitrary range of inputs to the generator:
这是用例:
/// 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;
}
这是汇编之前的代码膨胀:
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
即什么都没有.
这篇关于用非默认可构造类型填充std :: array(没有可变参数模板)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!