Is the std::array bit compatible with the old C array?

混江龙づ霸主 提交于 2019-12-09 07:30:34

问题


Is the underlying bit representation for an std::array<T,N> v and a T u[N] the same?

In other words, is it safe to copy N*sizeof(T) bytes from one to the other? (Either through reinterpret_cast or memcpy.)

Edit:

For clarification, the emphasis is on same bit representation and reinterpret_cast.

For example, let's suppose I have these two classes over some trivially copyable type T, for some N:

struct VecNew {
    std::array<T,N> v;
};

struct VecOld {
    T v[N];
};

And there is the legacy function

T foo(const VecOld& x);

If the representations are the same, then this call is safe and avoids copying:

VecNew x;
foo(reinterpret_cast<const VecOld&>(x));

回答1:


I say yes (but the standard does not guarantee it).

According to [array]/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.

And [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]).

In light of this, "can be list-initialized" is only possible if there are no other members in the beginning of the class and no vtable.

Then, data() is specified as:

constexpr T* data() noexcept;

Returns: A pointer such that [data(), data() + size()) is a valid range, and data() == addressof(front()).

The standard basically wants to say "it returns an array" but leaves the door open for other implementations.

The only possible other implementation is a structure with individual elements, in which case you can run into aliasing problems. But in my view this approach does not add anything but complexity. There is nothing to gain by unrolling an array into a struct.

So it makes no sense not to implement std::array as an array.

But a loophole does exist.




回答2:


This doesn't directly answer your question, but you should simply use 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));

If T is a trivially copyable type, this'll compile down to memcpy. If it's not, then this'll do element-wise copy assignment and be correct. Either way, this does the Right Thing and is quite readable. No manual byte arithmetic necessary.




回答3:


std::array provides method data() which can be used to copy to/from c-style array of proper size:

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

As stated on documentation

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.

so it seems that memory footprint would be compatible with c-style array, though it is not clear why you want to use "hacks" with reinterpret_cast when there is a proper way which does not have any overhead.




回答4:


The requirement on the data() method is that it return a pointer T* such that:

[data(), data() + size()) is a valid range, and data() == addressof(front()).

This implies that you can access each element sequentially via the data() pointer, and so if T is trivially copyable you can indeed use memcpy to copy sizeof(T) * size() bytes to/from an array T[size()], since this is equivalent to memcpying each element individually.

However, you cannot use reinterpret_cast, since that would violate strict aliasing, as data() is not required to actually be backed by an array - and also, even if you were to guarantee that std::array contains an array, since C++17 you cannot (even using reinterpret_cast) cast a pointer to an array to/from a pointer to its first member (you have to use std::launder).




回答5:


array doesn't mandate much about the underlying type over which you instantiate it.

To have any possibility of useful results from using memcpy or reinterpret_cast to do a copy, the type you instantiated it over would have to be trivially copyable. Storing those items in an array doesn't affect the requirement that memcpy (and such) only work with trivially copyable types.

array is required to be a contiguous container and an aggregate, which pretty much means that the storage for the elements must be an array. The standard shows it as:

T elems[N]; // exposition only

It later, however, has a note that at least implies that it's being an array is required (§[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]

[emphasis added]

Note how it's really only the specific name elems that isn't required.



来源:https://stackoverflow.com/questions/39376813/is-the-stdarray-bit-compatible-with-the-old-c-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!