Is it legal c++ to use reference as array/pointer?

Viewed 120

My team (including myself) is new to C++. A piece of our new development is a C++ function that needs to interface with a C function that takes an array as input. Something like the following construct was made to achieve this:

#include "stdio.h"

void the_c_function(double *array, int len)
{
   for (int i = 0; i < len; i++)
   {
      printf("%d: %g\n", i, array[i]);
   }
}

void the_cpp_wrapper(double& dref, int len)
{
   the_c_function(&dref, len);
}

int main()
{
   const int LEN = 4;
   double dbl_array[LEN] = { 3,4,5,6 };
   the_cpp_wrapper(dbl_array[0], LEN);
   return 0;
}

When compiled, this works as expected: it prints the contents of the array:

0: 3
1: 4
2: 5
3: 6

But this feels hardly legal to me or at the best something that should be discouraged.

Is this legal C++, i.e. is it guaranteed that a pointer to a reference of an array points to the original array?

Is there any reason why one would do it like this instead of using a pointer directly instead of using the reference as inbetween?

3 Answers

My team (including myself) is new to C++. ...

[...]

... something that should be discouraged.

You should get in the habit now of using the Standard C++ Library, in your case the best choice is std::vector:

#include <stdio.h>
#include <stdlib>
#include <vector>

void the_c_function(const double *array, size_t len) {/*...*/}
void the_cpp_wrapper(const std::vector<double>& v)
{
   the_c_function(v.data(), v.size());
}
// ----------------------------
int main()
{
   const std::vector<double> dbl_array { 3,4,5,6 };
   the_cpp_wrapper(dbl_array);
   return EXIT_SUCCESS;
}

You also should be clearer about const double* vs. double*, C++ intentionally wants you to use a much more verbose const_cast<double*> to cast-away const-ness.

If you want to go "all in" with C++, you can make the_cpp_wrapper() a bit more generic with a template:

template<typename TSpan>
void the_cpp_wrapper(const TSpan& v)
{
   the_c_function(v.data(), v.size());
}

With this code, you can pass anything to the_cpp_wrapper that has data() and size() methods. (Note that TSpan "can" be std::span<int> which could cause some obscure compiler errors; there are ways to fix that, but it's more C++.)


Not directly related, but you'll probably find std::span useful too.

The question of code readability aside,

is it guaranteed that a pointer to a reference of an array points to the original array?

Yes, see § 5.5 Expressions:

If an expression initially has the type “reference to T” ([dcl.ref], [dcl.init.ref]), the type is adjusted to T prior to any further analysis. The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression.

And §8.3.2 References:

4   It is unspecified whether or not a reference requires storage.

5   There shall be no references to references, no arrays of references, and no pointers to references.

In other words, an "address of a reference" isn't a thing; given double& dref, taking an address &dref will give the address of the original element inside the array.

Yes it is legal and it is guaranteed to reference to the original element in the array based on your code.

Some people like to design interface to force the caller to pass by reference to avoid checking whether the argument is a null pointer, which might be required when passing by pointer.

Related