Search code examples
c++arraysc++11rvaluetemporary-objects

Is it possible to construct a fixed sized array in-place as a function argument?


This is mainly a question about a corner of C++ syntax related to fixed-sized arrays.

Suppose I have a function that exploits type information, for example:

template<class T> void fun(T const& t){
    std::cout << typeid(t).name() << std::endl;
}

I can pass a value or a temporary object:

int i;
fun(i); // prints "int" ("i" actually)
fun(int{});   // prints "int" ("i" actually)

However I can't do the same with arrays

double a[10][10];
fun(a); // ok, prints "a[10][10]" ("A10_A10_d" actually)

fun(double[10][10]); // doesn't compile
fun(double{}[10][10]); // doesn't compile
fun(double[10][10]{}); // doesn't compile
fun(double()[10][10]); // doesn't compile
fun(double[10][10]()); // doesn't compile
fun(double(&)[10][10]); // doesn't compile
fun(double(*)[10][10]); // doesn't compile

I could in principle do:

typedef double a1010[10][10];
fun(a1010{});

but, is it possible to do without predefining a typedef?

Is it possible at all to construct a fixed sized array in-place as a function argument?

Full code:

template<class T> void fun(T const& t){
    std::cout << typeid(t).name() << std::endl;
}

typedef double a1010[10][10];

int main(){
    int i;
    fun(i); // prints "int" ("i" actually)
    double a[10][10];
    fun(a); // prints "a[10][10]" ("A10_A10_d" actually)
    fun(a1010{});

    fun(int{});   // prints "int"
/*  fun(double[10][10]); // doesn't compile
    fun(double{}[10][10]); // doesn't compile
    fun(double[10][10]{}); // doesn't compile
    fun(double()[10][10]); // doesn't compile
    fun(double[10][10]()); // doesn't compile
    fun(double(&)[10][10]); // doesn't compile
    fun(double(*)[10][10]); // doesn't compile
    */
    return 0;
}

Bonus points (probably a bounty): What about variable-sized arrays?

int N = 10;
f(double[N]);

Solution

  • Try:

    fun((int[3]){1,2,3});
    fun((int[5]){});
    

    As for the "bonus points": variable sized arrays are not part of the language. This extension to the language does not work with template arguments:

    prog.cc:4:6: note: candidate template ignored: substitution failure : variably modified type 'int [n]' cannot be used as a template argument fun(const T&t)

    Edit

    As Chris noted, the above solution proposes to use compound literals, which are an extension to C++. There is a solution that avoids this extension to C++, using a simple helper class:

    template <class T, std::size_t N>
    struct my_array
    {
        T data[N];
    };
    
    template <class T, std::size_t N>
    void print(const T (&x)[N])
    {
         for (auto i: x)
             std::cout << i << '\n';
    }
    
    int main()
    {
        print(my_array<int,3>{9,10,11}.data);
    }
    

    This works well, but requires one to add template argument to my_array, which are not deduced. With C++17 it is possible to automatically deduce type and size:

    template <class T, std::size_t N>
    struct my_array
    {
        constexpr my_array(std::initializer_list<T> x)
        {
           std::size_t i = 0;
           for (auto val : x)
               data[i++] = val;
        }
        T data[N];
    };
    template <class ...T>
    my_array(T...) -> my_array<typename std::common_type<T...>::type, sizeof...(T)>;
    
    int main()
    {
        print(my_array{9,10,11}.data);
    }
    

    For two dimensional arrays this is slightly more complicated:

    template <class T, std::size_t N1, std::size_t N2>
    struct my_array2d
    {
        constexpr my_array2d(std::initializer_list<std::initializer_list<T> > x)
        {
            std::size_t i = 0;
            for (const auto & row : x) {
                int j=0;
                for (const auto & val: row) {
                    data[i][j++] = val;
                }
                i++;
            }
        }
        T data[N1][N2];
    };
    int main()
    {
        work(my_array2d<int, 3, 2>{{9,1},{10,2},{11,3}}.data);
    }
    

    I have given up on deduction guides for two dimensional arrays, but I believe they are possible.