std::array 位与旧 C 数组兼容吗?
Is the std::array bit compatible with the old C array?
std::array<T,N> v
和 T u[N]
的基础 位表示 是否相同?
换句话说,将 N*sizeof(T)
个字节从一个复制到另一个是否安全? (通过 reinterpret_cast
或 memcpy
。)
编辑:
为了澄清,重点是相同的位表示和reinterpret_cast
。
例如,假设我有这两个 类 在一些简单的可复制类型 T
上,对于某些 N
:
struct VecNew {
std::array<T,N> v;
};
struct VecOld {
T v[N];
};
还有遗留功能
T foo(const VecOld& x);
如果表示相同,则此调用是安全的并且避免了复制:
VecNew x;
foo(reinterpret_cast<const VecOld&>(x));
std::array
提供方法 data() 可用于复制 to/from 适当大小的 c 样式数组:
const size_t size = 123;
int carray[size];
std::array<int,size> array;
memcpy( carray, array.data(), sizeof(int) * size );
memcpy( array.data(), carray, sizeof(int) * size );
所述
This container is an aggregate type with the same semantics as a struct holding a C-style array T[N] as its only non-static data member.
所以内存占用似乎与 c 样式数组兼容,但不清楚为什么要使用 "hacks" 和 reinterpret_cast
当有正确的方法不有任何开销。
array
对实例化它的基础类型没有太多要求。
要使用 memcpy
或 reinterpret_cast
进行复制而获得 任何 有用结果的可能性,您实例化它的类型必须是平凡可复制。将这些项目存储在 array
中不会影响 memcpy
(等)仅适用于普通可复制类型的要求。
array
需要是一个 连续的容器 和一个聚合,这几乎意味着元素的存储必须是一个数组。标准显示为:
T elems[N]; // exposition only
然而,它后来有一个注释,至少暗示它是一个数组是必需的 (§[array.overview]/4):
[Note: The member variable elems
is shown for exposition only, to emphasize that array
is a class aggregate.
The name elems
is not part of array’s interface. —end note]
[强调]
请注意,实际上只有特定名称 elems
不是必需的。
这不会直接回答您的问题,但您应该简单地使用 std::copy
:
T c[N];
std::array<T, N> cpp;
// from C to C++
std::copy(std::begin(c), std::end(c), std::begin(cpp));
// from C++ to C
std::copy(std::begin(cpp), std::end(cpp), std::begin(c));
如果 T
是普通可复制类型,这将编译为 memcpy
。如果不是,那么这将按元素进行复制分配并且是正确的。无论哪种方式,这都是正确的,而且可读性很强。无需手动字节运算。
data()
方法的要求是 return 一个指针 T*
使得:
[data(), data() + size())
is a valid range, and data() == addressof(front())
.
这意味着您可以通过 data()
指针顺序访问每个元素,因此如果 T
是可平凡复制的,您确实可以使用 memcpy
来复制 sizeof(T) * size()
bytes to/from 一个数组 T[size()]
,因为这等同于 memcpy
单独处理每个元素。
但是,您不能使用 reinterpret_cast
,因为那样会违反严格的别名,因为 实际上 不需要 data()
由数组支持 -而且,即使你要保证 std::array
包含一个数组,因为 C++17 你不能(即使使用 reinterpret_cast
)将指针转换为数组 to/from 指向它的指针第一个成员(你必须使用 std::launder
)。
我说是(但标准不保证)
根据[数组]/2:
An array is an aggregate ([dcl.init.aggr]) that can be
list-initialized with up to N elements whose types are convertible to
T.
和[dcl.init.aggr]:
An aggregate is an array or a class (Clause [class]) with
no user-provided, explicit, or inherited constructors ([class.ctor]),
no private or protected non-static data members (Clause
[class.access]),
no virtual functions ([class.virtual]), and
no virtual, private, or protected base classes ([class.mi]).
鉴于此,"can be list-initialized" 只有在 class 开头没有其他成员且没有 vtable 的情况下才有可能。
那么,data()
指定为:
constexpr T* data() noexcept;
Returns: A pointer such that [data(), data() + size())
is a valid range, and data() == addressof(front())
.
该标准基本上想说 "it returns an array" 但为其他实现敞开大门。
唯一可能的其他实现是具有单个元素的结构,在这种情况下,您 可以 运行 出现别名问题。但在我看来,这种方法只会增加复杂性。将数组展开为结构没有任何好处。
所以没有意义不将std::array
实现为数组。
但是确实存在漏洞
std::array<T,N> v
和 T u[N]
的基础 位表示 是否相同?
换句话说,将 N*sizeof(T)
个字节从一个复制到另一个是否安全? (通过 reinterpret_cast
或 memcpy
。)
编辑:
为了澄清,重点是相同的位表示和reinterpret_cast
。
例如,假设我有这两个 类 在一些简单的可复制类型 T
上,对于某些 N
:
struct VecNew {
std::array<T,N> v;
};
struct VecOld {
T v[N];
};
还有遗留功能
T foo(const VecOld& x);
如果表示相同,则此调用是安全的并且避免了复制:
VecNew x;
foo(reinterpret_cast<const VecOld&>(x));
std::array
提供方法 data() 可用于复制 to/from 适当大小的 c 样式数组:
const size_t size = 123;
int carray[size];
std::array<int,size> array;
memcpy( carray, array.data(), sizeof(int) * size );
memcpy( array.data(), carray, sizeof(int) * size );
所述
This container is an aggregate type with the same semantics as a struct holding a C-style array T[N] as its only non-static data member.
所以内存占用似乎与 c 样式数组兼容,但不清楚为什么要使用 "hacks" 和 reinterpret_cast
当有正确的方法不有任何开销。
array
对实例化它的基础类型没有太多要求。
要使用 memcpy
或 reinterpret_cast
进行复制而获得 任何 有用结果的可能性,您实例化它的类型必须是平凡可复制。将这些项目存储在 array
中不会影响 memcpy
(等)仅适用于普通可复制类型的要求。
array
需要是一个 连续的容器 和一个聚合,这几乎意味着元素的存储必须是一个数组。标准显示为:
T elems[N]; // exposition only
然而,它后来有一个注释,至少暗示它是一个数组是必需的 (§[array.overview]/4):
[Note: The member variable
elems
is shown for exposition only, to emphasize thatarray
is a class aggregate. The nameelems
is not part of array’s interface. —end note]
[强调]
请注意,实际上只有特定名称 elems
不是必需的。
这不会直接回答您的问题,但您应该简单地使用 std::copy
:
T c[N];
std::array<T, N> cpp;
// from C to C++
std::copy(std::begin(c), std::end(c), std::begin(cpp));
// from C++ to C
std::copy(std::begin(cpp), std::end(cpp), std::begin(c));
如果 T
是普通可复制类型,这将编译为 memcpy
。如果不是,那么这将按元素进行复制分配并且是正确的。无论哪种方式,这都是正确的,而且可读性很强。无需手动字节运算。
data()
方法的要求是 return 一个指针 T*
使得:
[data(), data() + size())
is a valid range, anddata() == addressof(front())
.
这意味着您可以通过 data()
指针顺序访问每个元素,因此如果 T
是可平凡复制的,您确实可以使用 memcpy
来复制 sizeof(T) * size()
bytes to/from 一个数组 T[size()]
,因为这等同于 memcpy
单独处理每个元素。
但是,您不能使用 reinterpret_cast
,因为那样会违反严格的别名,因为 实际上 不需要 data()
由数组支持 -而且,即使你要保证 std::array
包含一个数组,因为 C++17 你不能(即使使用 reinterpret_cast
)将指针转换为数组 to/from 指向它的指针第一个成员(你必须使用 std::launder
)。
我说是(但标准不保证)
根据[数组]/2:
An array is an aggregate ([dcl.init.aggr]) that can be list-initialized with up to N elements whose types are convertible to T.
和[dcl.init.aggr]:
An aggregate is an array or a class (Clause [class]) with
no user-provided, explicit, or inherited constructors ([class.ctor]),
no private or protected non-static data members (Clause [class.access]),
no virtual functions ([class.virtual]), and
no virtual, private, or protected base classes ([class.mi]).
鉴于此,"can be list-initialized" 只有在 class 开头没有其他成员且没有 vtable 的情况下才有可能。
那么,data()
指定为:
constexpr T* data() noexcept;
Returns: A pointer such that[data(), data() + size())
is a valid range, anddata() == addressof(front())
.
该标准基本上想说 "it returns an array" 但为其他实现敞开大门。
唯一可能的其他实现是具有单个元素的结构,在这种情况下,您 可以 运行 出现别名问题。但在我看来,这种方法只会增加复杂性。将数组展开为结构没有任何好处。
所以没有意义不将std::array
实现为数组。
但是确实存在漏洞