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

后端 未结 5 1820
心在旅途
心在旅途 2020-12-30 21:24

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

In other words, is it safe to copy

相关标签:
5条回答
  • 2020-12-30 21:25

    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.

    0 讨论(0)
  • 2020-12-30 21:26

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

    0 讨论(0)
  • 2020-12-30 21:27

    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.

    0 讨论(0)
  • 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.

    0 讨论(0)
  • 2020-12-30 21:49

    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.

    0 讨论(0)
提交回复
热议问题