#include <iostream>
#include <typeinfo>
#include <type_traits>

int main()
{
    int a[20] = { 1, 2, 3, 4 } ; // array of 20 int

    decltype(a) b = { 1, 2, 3, 4 } ; // b is of the same type as a - an array of 20 int

    typedef int array_type[20] ; // 'array_type' is an alias for 'array of 20 int'
    using array_type = int[20] ; // 'array_type' is an alias for 'array of 20 int'

    array_type c = { 1, 2, 3, 4 } ; // c is an array of 20 int

    // b and c are of the same type
    std::cout << std::boolalpha ;
    std::cout << "b and c are of the same type: "
               << std::is_same< decltype(b), decltype(c) >::value << '\n' ; // true

    array_type d[5] = { { 1, 2 }, {}, {6} } ; // array of 5 array_type

    int e[5][20] = { { 1, 2 }, {}, {6} } ; // array of 5 array of 20 int

    // d and e are of the same type
    std::cout << "d and e are of the same type: "
               << std::is_same< decltype(d), decltype(e) >::value << '\n' ; // true

    int f[5][15] = { { 1, 2 }, {}, {6} } ; // array of 5 array of 15 int

    // d and f are of different types
    std::cout << "d and f are of the same type: "
               << std::is_same< decltype(d), decltype(f) >::value << '\n' ; // false

    // an (expression of type) array can be implicitly converted to a pointer,
    // the result of the conversion is a pointer to the first element of the array.

    int* p1 = &(a[0]) ; // address of first element
    int* p2 = a ; // a is converted to a pointer, both p1 and p2 point to the first element

    std::cout << "p1 == p2 : " << ( p1 == p2 ) << '\n' ; // true
}
