简体   繁体   中英

How does std::array constructor initialize its array?

Im trying to understand how std::array constructor work and how can he take a array and initialize it to its array.

I was searching on the standard library file and i find this piece of code

#if _HAS_CXX17
template <class _First, class... _Rest>
struct _Enforce_same {
    static_assert(conjunction_v<is_same<_First, _Rest>...>,
        "N4687 26.3.7.2 [array.cons]/2: "
        "Requires: (is_same_v<T, U> && ...) is true. Otherwise the program is ill-formed.");
    using type = _First;
};

template <class _First, class... _Rest>
array(_First, _Rest...) -> array<typename _Enforce_same<_First, _Rest...>::type, 1 + sizeof...(_Rest)>;
#endif // _HAS_CXX17

Is this the constructor? how does it work exacly?

Thanks!

As already mentioned in comments above, std::array is an aggregate type, so you are not calling a constructor but actually initializing a data member.

The code you point at in the question allows creation of std::array without stating array's type and size. This is done, with deduction guides, as seen in the code below.

Here is how it may look if you implement it by yourself:

template<typename T, std::size_t SIZE>
struct MyArray {
    T arr[SIZE];    
};

// MyArray deduction guides:
// similar code was added to std::array in C++17 to allow the
// creation of a2 below, without providing template arguments
template <class _First, class... _Rest>
MyArray(_First, _Rest...) -> MyArray<_First, 1 + sizeof...(_Rest)>;

int main() {
    MyArray<int, 5> a1 = {1, 2, 3}; // since C++11
    MyArray a2 {1, 2, 3}; // deduced to MyArray<int, 3>, since C++17
    // creation of a2 is based on the deduction guides above
}

The code above ignores the case of sending different types to MyArray, like this:

MyArray a2 {1, 2.0, 3}; // still deduced to MyArray<int, 3> with our code

There are several options to treat the above: decide that the type of the array is based on the type of the first value (as we actually do in our naive implementation), decide that the type would be based on the common_type of the values, or disallow such initialization. The C++ standard decided to disallow it, so there is a need to check that all types are the same and raise compilation error if not, eg using static_assert . As done in the original code posted, using the _Enforce_same struct.

The answer by @Amir Kirsh is well detailed. So I don't want to repeat it. Instead, I'm inviting you to have a look at the assembly output of the following code:

#include <iostream>
#include <array>

int main( )
{
    // std::array<int, 5> arr = { 1, 2, 3 }; // std::array
    int arr[5] { 1, 2 , 3 }; // raw array (aka C-style array)

    for ( const auto& i : arr )
    {
        std::cout << i << '\n';
    }

    return 0;
}

You can check and compare both array's assembly codes here .
Just uncomment the std::array version and comment out the raw array version to see the assembly code for the std::array version.

But to make your job even easier, here is the code for both arrays:
std::array

        mov     rax, QWORD PTR .LC0[rip]
        mov     QWORD PTR [rsp+28], 0
        lea     rbx, [rsp+16]
        lea     rbp, [rsp+36]
        mov     QWORD PTR [rsp+16], rax
        mov     DWORD PTR [rsp+24], 3

raw array

        mov     rax, QWORD PTR .LC0[rip]
        mov     QWORD PTR [rsp+28], 0
        lea     rbx, [rsp+16]
        lea     rbp, [rsp+36]
        mov     QWORD PTR [rsp+16], rax
        mov     DWORD PTR [rsp+24], 3

YES, As can be seen. they are identical. The std::array is converted to its equivalent C-style array during the compile-time. It's just syntax-sugar . There is literally no need for a constructor or a destructor . Both are the same thing and are stored on the stack. They are trivially constructible which means they can be constructed just by incrementing the stack pointer register and a few other fast instructions.

Just like this:

#include <iostream>
#include <type_traits>
#include <array>

int main( )
{
    std::cout << std::boolalpha << "is std::array<int, 5> trivially constructible: "
              << std::is_trivially_constructible< std::array<int, 5> >::value << '\n';

    std::cout << std::boolalpha << "is int[5] trivially constructible: "
              << std::is_trivially_constructible< int[5] >::value << '\n';

The result:

is std::array<int, 5> trivially constructible: true
is int[5] trivially constructible: true

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM