5

So while it is common/typical to accept an array that has decayed to a pointer like the following

#include <iostream>

void example(int* data)
{
    std::cout << data[0] << ' ' << data[1] << ' ' << data[2] << std::endl;
}

int main()
{
    int data[3] = {1, 2, 3};
    example(data);
}

I noticed that you can also seemingly do the opposite. Namely you can pass a pointer that will undecay(?) back to an array

#include <iostream>

void example(int data[3])
{
    std::cout << data[0] << ' ' << data[1] << ' ' << data[2] << std::endl;
}

int main()
{
    int data[3] = {1, 2, 3};
    example(data);
    example(nullptr);  // Is this legal? Can I prevent this?
}

Note that the data argument of example has changed from int* to int[3] yet I am still able to pass nullptr. Changing the argument to std::array<int, 3> will obviously prevent this, but I'm curious if this implicit conversion is described by the standard? There is a lot of commentary on "array to pointer" implicit conversion, but this seems to be the other way around.

Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
  • i wouldn't call it "common/typical". Of course that depends on what kind of code you are used to – 463035818_is_not_an_ai Aug 11 '21 at 16:15
  • @463035818_is_not_a_number I deal with code ranging from "written yesterday" back to "written 2 decades ago" so I agree it depends on a lot on the codebase, but in general that is (or was?) a very common idiom to see around. – Cory Kramer Aug 11 '21 at 16:17
  • `void example(int* data, int size)` would be more common, but it is C-like code anyway, and not really C++. – Jarod42 Aug 11 '21 at 16:19
  • 1
    Re: `example(nullptr);`: "_Is this legal?_" - Yes. "_Can I prevent it_" - Yes, by adding a deleted overload: : `void example(std::nullptr_t) = delete;` – Ted Lyngmo Aug 11 '21 at 16:24
  • Here's a semi-ugly template version of [`example()`](https://godbolt.org/z/sbccj1jWb) to make it accept a `const int[]` of any valid size.. `nullptr` is also rejected of course. – Ted Lyngmo Aug 11 '21 at 17:04

2 Answers2

11

In fact,

void example(int data[3])

is

void example(int data[])

or

void example(int* data)

You need

void example(/*const*/ int (&data)[3])

to have array reference, (and so rejecting pointer or array of different size).

Jarod42
  • 203,559
  • 14
  • 181
  • 302
5

When you have

void example(int data[3])

that array itself decays to a pointer so the function is actually

void example(int* data)

which is why you can still pass nullptr to the function. The relevant paragraph for this is [dcl.fct]/5

The type of a function is determined using the following rules. The type of each parameter (including function parameter packs) is determined from its own parameter-declaration ([dcl.decl]). After determining the type of each parameter, any parameter of type “array of T” or of function type T is adjusted to be “pointer to T”. [...]

emphasis mine

NathanOliver
  • 171,901
  • 28
  • 288
  • 402