通过可变参数模板参数初始化std::array
,从给定索引开始可以通过以下方式完成:
#include <array>
template <typename T, size_t N>
struct A
{
template <typename ... Ts>
A(size_t i, Ts ... vals)
{
constexpr size_t P = sizeof...(vals);
std::array<T, P> temp{ vals... };
for (size_t j = 0; j < P; ++j)
{
arr[i + j] = temp[j];
}
}
std::array<T, N> arr;
};
但是有可能在不将参数包转换为临时元组或其他std :: array的情况下实现相同的目的吗?
答案 0 :(得分:3)
您可以使用std::index_sequence
并委派构造函数:
template <typename T, size_t N>
struct A
{
template <typename ... Ts>
A(size_t i, Ts&& ... vals)
:
A(std::index_sequence_for<Ts...>{}, i, std::forward<Ts>(vals)...)
{}
template <std::size_t...Is, typename ... Ts>
A(std::index_sequence<Is...>, size_t i, Ts&& ... vals)
{
int dummy[] = {0, ((arr[i + Is] = vals), void(), 0)...};
static_cast<void>(dummy); // Avoid warning for unused variable
}
std::array<T, N> arr;
};
或者使用C ++ 17的折叠表达式,
template <std::size_t...Is, typename ... Ts>
A(std::index_sequence<Is...>, size_t i, Ts&& ... vals)
{
(static_cast<void>(arr[i + Is] = vals), ...);
}
答案 1 :(得分:2)
template<std::size_t I>
using index_t = std::integral_constant<std::size_t, I>;
template<class T, std::size_t=0, class...Args>
T make(Args&&...args){ return T(std::forward<Args>(args)...); }
template <class T, size_t N>
struct A {
template <std::size_t I, class...Ts>
A(index_t<I>, Ts&&... vals):
A(std::make_index_sequence<I>{}, std::forward<Ts>(vals)...)
{}
template <std::size_t...Is, class...Ts>
A(std::index_sequence<Is...>, Ts&&...vals):
arr{{ make<T,Is>()..., std::forward<Ts>(vals)... }}
{}
// ...
};
这需要编译时已知的偏移量,但是避免创建默认构造的数组然后分配给它。然而,它还涉及创建一堆T
并将它们移动到阵列中。伊克!
备份第二个,我们知道该数组是T
s的标准布局块。让我们作弊。
// stores the raw buffer for the array, and constructs it with
// some care:
template<class T>
using storage = std::aligned_storage_t<sizeof(T),alignof(T)>;
template<class T, size_t N>
struct A_storage {
storage<std::array<T, N>> raw_arr;
std::array<T,N>& arr() {
return *reinterpret_cast<std::array<T, N>*>(&raw_arr);
}
std::array<T,N> const& arr() const {
return *reinterpret_cast<std::array<T, N> const*>(&raw_arr);
}
template<class...Ts>
static void make_arr(storage<std::array<T, N>>& retval, std::size_t offset, Ts&&...ts) {
auto* ptr = reinterpret_cast<T*>(&retval);
try {
std::size_t ctor_count = 0;
for (size_t i = 0; i < offset; ++i) {
++ctor_count;
::new((void*)(ptr+i)) T();
}
::new((void*)(ptr +offset)) std::array<T, sizeof...(Ts)>{{
(++ctor_count, void(), std::forward<Ts>(ts))...
}};
for (size_t i = offset+sizeof...(Ts); i < N; ++i) {
++ctor_count;
::new((void*)(ptr+i)) T();
}
} catch(...) {
// ctor_count is the count of constructors *attempted*
// so ptr[ctor_count-2] is the last one we *succeeded at*
// destroy everything we successfully constructed.
// ctor_count has to be at least 1, as we cannot throw before
// incrementing. Let us peel it off.
--ctor_count;
// iterate backwards from ctor_count-1 down to 0, so we destroy
// in reverse order of constructing:
for (size_t i = 1; i <= ctor_count; ++i) {
ptr[ctor_count-i].~T();
}
throw;
// I use attempted, because the initializer list syntax of array
// construction doesn't let me increment after I provide the value
// for the place I'm constructing. I can, however, increment before.
}
}
template<class...Ts>
A_storage(std::size_t offset, Ts&&...ts)
{
static_assert(sizeof...(Ts)<=N, "too much data!");
ASSERT(offset+sizeof...(Ts)<=N);
make_arr(raw_arr, offset, std::forward<Ts>(ts)...);
}
// only runs if the ctor above completes, which means
// everything was constructed:
~A_storage() {
for (size_t i = 1; i <= N; ++i) {
arr()[N-i].~T();
}
}
};
template<std::size_t N, class T>
struct A:A_storage {
template<class...Ts>
A(std::size_t offset, Ts&&...ts):
A_storage(offset, std::forward<Ts>(ts)...)
{}
};
一切都是就地建造的。无需T(T&&)
支持! (除非你将参数传递给数组,但这不是我的问题)
我试图在异常恶劣的环境中进行破坏。我可能弄错了。
NRVO应该省略唯一的临时变量(make_arr
的返回值)。另一方面,编译器可能是一个懒惰而不是忽略类成员构造函数。糟糕的编译器。
答案 2 :(得分:2)
虽然它涉及C ++ 17倍表达式,但受Jarod42评论启发的最简单的解决方案是:
#include <array>
template <typename T, size_t N>
struct A
{
template <typename ... Ts>
A(Ts ... vals)
{
size_t i = 1; // starting index, can be an argument
((arr[i++] = vals) , ...);
}
std::array<T, N> arr;
};
int main()
{
A<int, 4> a(1, 2, 3);
return 0;
}
使用 Clang 3.6 或更高级别使用优化级别-O1
和-std=c++1z
标记生成的程序集:
A<int, 4ul>::A<int, int, int>(int, int, int): # @A<int, 4ul>::A<int, int, int>(int, int, int)
pushq %rbp
pushq %r15
pushq %r14
pushq %rbx
pushq %rax
movl %ecx, %r14d
movl %edx, %r15d
movl %esi, %ebx
movq %rdi, %rbp
movl $1, %esi
callq std::array<int, 4ul>::operator[](unsigned long)
movl %ebx, (%rax)
movl $2, %esi
movq %rbp, %rdi
callq std::array<int, 4ul>::operator[](unsigned long)
movl %r15d, (%rax)
movl $3, %esi
movq %rbp, %rdi
callq std::array<int, 4ul>::operator[](unsigned long)
movl %r14d, (%rax)
addq $8, %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
retq
这相当于测试用例
template <typename T, size_t N>
struct B
{
B(T x, T y, T z)
{
arr[1] = x;
arr[2] = y;
arr[3] = z;
}
std::array<T, N> arr;
};
int main()
{
B<int, 4> b(1, 2, 3);
return 0;
}
生成的程序集
B<int, 4ul>::B(int, int, int): # @B<int, 4ul>::B(int, int, int)
pushq %rbp
pushq %r15
pushq %r14
pushq %rbx
pushq %rax
movl %ecx, %r14d
movl %edx, %r15d
movl %esi, %ebx
movq %rdi, %rbp
movl $1, %esi
callq std::array<int, 4ul>::operator[](unsigned long)
movl %ebx, (%rax)
movl $2, %esi
movq %rbp, %rdi
callq std::array<int, 4ul>::operator[](unsigned long)
movl %r15d, (%rax)
movl $3, %esi
movq %rbp, %rdi
callq std::array<int, 4ul>::operator[](unsigned long)
movl %r14d, (%rax)
addq $8, %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
retq
值得注意的是,两个案例的生成组合是等效的。