phi*_*inz 5 c++ stdarray c++20 std-span
std::array<uint8_t,N>我正在尝试从 a创建 a,std::span<uint8_t,N>但我找不到一种没有memcpy、或 的方法来执行此操作std::copy,或者std::ranges::copy这不能保护我免受目标数组大小的错误指定。
#include <algorithm>
#include <array>
#include <iostream>
#include <span>
int main(int argc, char **argv) {
constexpr size_t N = 10;
std::array<uint8_t, N> original;
std::span span(original); // of type std::span<uint8,N>
std::array copy1(span); // does not work
std::array<uint8_t, N> copy2(span); // does not work
std::array<uint8_t, N> copy3(begin(span), end(span)); // does not work
// ugly stuff that works, but does not protect me if I specify wrong array size
constexpr size_t M{N - 1}; //oops, leads to array overflow
std::array<uint8_t, M> copy4;
std::copy(begin(span), end(span), copy4.begin());
std::ranges::copy(span, copy4.begin());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在现代 C++ 中执行此操作的惯用方法是什么?
但我找不到一种没有
memcpy,或 的方法来做到这一点std::copy,或者std::ranges::copy不能保护我免受错误的目标数组大小规范的影响。
如果aspan具有静态范围,则size()可以将其实现为常量表达式,该常量表达式适用于当前主流编译器:
std::array<uint8_t, span.size()> copy4;
std::ranges::copy(span, copy4.begin());
Run Code Online (Sandbox Code Playgroud)
extent或者您可以通过其静态成员常量(如)获取大小值std::array<uint8_t, span.extent>,这保证可以工作。
为了扩展@Jarod42的答案,我们可以做一些改进:
#include <span>
#include <array>
#include <cstring>
#include <algorithm>
// 1. constrain this function to copy-constructible types
template <std::copy_constructible T, std::size_t N>
requires (N != std::dynamic_extent)
// 2. handle spans of const/volatile T correctly
std::array<std::remove_cv_t<T>, N> to_array(std::span<T, N> s)
// 3. add conditional noexcept specification
noexcept(std::is_nothrow_copy_constructible_v<T>)
{
// add type alias so we don't repeat the return type
using result_type = decltype(to_array(s));
if constexpr (std::is_trivial_v<T>) {
// 4. avoid unnecessary instantiations of std::index_sequence etc.
// in the cases where we can copy with no overhead (should be fairly common)
result_type result;
// note: we cannot simply use std::memcpy here because it would not
// correctly handle volatile T
std::ranges::copy(s, result.begin());
return result;
}
// TODO: consider using std::ranges::copy for all default-constructible
// and copyable types, because the following results in huge assembly output
else {
// if 4. is not applicable, we still have to use @Jarod42's solution
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return result_type{s[Is]...};
}(std::make_index_sequence<N>{});
}
}
Run Code Online (Sandbox Code Playgroud)
如果您想进一步减小装配尺寸,可以使用以下条件代替:
std::is_default_constructible_v<T> && std::is_copy_assignable_v<T>
Run Code Online (Sandbox Code Playgroud)
如果您担心过度初始化会产生开销std::ranges::copy,您可以使用std::is_trivially_default_constructible_v<T>,可能与 一起使用std::ranges::uninitialized_copy,这应该可以减轻这种情况。