2

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]);
6
  • Look at std::array, this is likely what you are looking for. Besides, what is your point, really? Is it for type checking, or for performance? If you wish to improve performance, your attempt is almost certainly misguided. Commented Oct 10, 2018 at 4:10
  • Have you considered Variable Template? Commented Oct 10, 2018 at 4:11
  • On the bright side, this is not something most people would want to do. Commented Oct 10, 2018 at 4:27
  • @Frax, it was purely for syntactic sugar. To pass a type (e.g. double) and "numbers" (10, 10) through a single parameter. The contents are never used, it looks like the static array is ever allocated or reserved, because in clang and gcc I was able to pull double a [100000000][1000000000]; gun(a); gun((double[100000000][1000000000]){}); without stack overflow. I was also hopping that the numbers could be runtime as well. Commented Oct 10, 2018 at 4:43
  • 1
    I suggest to change your function to something like template<class T> struct Tag<T> {}; template<class T> void fun(tag<T>){ std::cout << typeid(T).name() << std::endl; }, so you can pass types easily (no issue with non default constructible type, you might keep rvalue/lvalue constness information, ...). With usage similar to fun(tag<double[10][10]>{}); or fun(tag<decltype(var)>{}); Commented Oct 10, 2018 at 9:58

2 Answers 2

5
+50

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.

Sign up to request clarification or add additional context in comments.

7 Comments

Perfect, this is what I was looking for, fun((double[10][10]){}). I imagined that the variable sized version wouldn't work.
Since you mention VLAs not being part of the language, neither are compound literals like (int[5]){}. These are a C99 feature just like VLAs.
@chris I have added a solution that avoids compound literals which, as you pointed out, is an extension to C++
Thank you for the edit, honestly what I wanted was to have a creative syntax to pass a type and a couple of sizes together as part of a single parameter. (I was not interested in passing the contents.)
BTW, the original solution technically is not pure C++, with -pedantic clang and gcc give warning: ISO C++ forbids compound-literals [-Wpedantic] fun((double[10][10]){}); godbolt.org/z/zgTali
|
1

You have tried many combinations with double, but you seem to have missed out on one.

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

This compiles and gives: A10_A10_d

1 Comment

Is there a reason you did not try this?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.