0

I'm new to C++ and I have 2 questions on passing arrays to a function (which may be the same thing).

  1. When passing an array to a function by reference, doesn't parameter require explicit size information as in void my_func (const T &array) instead of void my_func (const T (&array)[10])?

  2. What is the difference between a function having an array pointer parameter void my_func (const T* array) and void my_func (const T(* array)[10])(with or without explicit array size)?

Below is the code which accepts any size of array without explicitly passing array size:

template<typename _Ret, //return type
         typename _Coll> //collection type, no size argument
_Ret Sum(const _Coll& c) {

        _Ret sum = 0;
        for (auto& v : c)
                sum += v;
        return sum;
}

int main() {
        // With regular array, Passed as reference
        int arr[] = {1, 2, 3, 4, 5};
        std::cout << Sum<int64_t>(arr) << "\n"; //15

        // With vector 
        std::vector<int> vec = {1, 2, 3, 4, 5};
        std::cout << Sum<int64_t>(vec) << "\n"; //15
        return 0;
}

The article from which this example originates says as below:

Additionally, the use of templates can even avoid the unsightly array reference syntax and make the code work for any array size.

If this works, then is the general template for any array size as below unnecessary?

template<typename T, std::size_t S>
void my_func(T (&arr)[S]) {
        ... ...;
}

And also a side question, why doestemplate<typename _Ret, typename _Coll> requires 2 template arguments but Sum<int64_t>(arr) specifies only 1 template argument?

2
  • Generally, C-arrays aren't passed by reference, since the pass-by-value pointer and size get the job done just fine. There's likely a compatibility reason in there as well. It's not necessary to ever state the size of the 1st dimension of a C-array. Hopefully, std::span makes all of this go away. It's a cool function, though. Commented Apr 26, 2021 at 3:34
  • The fact you've named an argument array doesn't force the compiler to only accept array arguments and reject everything else. const T &array (where T is a template parameter) can match a literal of type int - which is undesirable if the function cannot do anything meaningful if the argument is not a collection. Whereas const T (&array)[10] means the compiler will reject any argument that is not an array of 10 elements, while const T (&array)[S] (where S is a templated argument with integral type) is acceptable for an array of any size. Commented Apr 26, 2021 at 4:30

2 Answers 2

1
  1. Assuming T is not an array, void my_func (const T &array) would not be the right way of trying that, since that expects a reference to an T, not an array. The right way of testing this would probably be void my_func(const T (&array)[]):
#include <iostream>

void my_func(const int (&)[]) {}

int main()
{
    int a = 10;
    int b[] = {1, 2, 3};
    my_func(b);

    return 0;
}

When I try running this, I get an error: error: parameter ‘<anonymous>’ includes reference to array of unknown bound ‘const int []’. Replacing const int (&)[] with const int (&)[3] makes it work. So no, you have to specify the size of the array.


  1. Those are two completely different things. void my_func(const T *array) is equivalent to void my_func(const T array[]) while void my_func(const T (*array)[10]) is equivalent to void my_func(const T array[][10]).

C++ Insights is a very nice place to see how templates expand. When we do Sum<int64_t>(arr), a new function of Sum is created from the template that is int64_t Sum<int64_t, int [5]>(int const (&c)[5]). As you can see, you don't have to specify the size now because the template will do it for you. Every time you pass a different size array to Sum, a new function will be created to work with that size.

and also a side question, template<typename _Ret, typename _Coll> requires 2 template arguments but why does Sum<int64_t>(arr) specifies only 1 template argument?

Because the second argument is deduced. Since the compiler knows that you're passing an int [5] to the function, you don't need to pass the type. However, there is no way for it to know the return type, so you have to pass the type of that.

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

1 Comment

yes, for the 2nd question, I edited my mistake. So const T* array[10] is now const T(* array)[10], which is a pointer to an array.
1
  1. when passing an array to a function by reference, does parameter not require explicit size information as in void my_func (const T &array) instead of void my_func (const T (&array)[10])?

A reference parameter does not necessarily have to be reference to an array.

const T & is a reference to a const T. If T type alias of int[10] for example, then const T & is const int (&)[10] i.e. reference to an array of 10 const int. If T is a template type parameter, then the template can be instantiated with different types, including array types of different sizes and also including non-array types.

const T (&)[10] is a reference to an array of 10 const T.

You don't need to pass size information separately when the size information is encoded in the type of the parameter, such as is the case when the parameter is a reference to an array.

But the example in my question neither defines alias

You're mistaken. Your example is a template function and a template type parameter is a type alias.

Function calling statement simply passes arr which is converted(decayed) into a pointer to int.

You're mistaken. arr does not decay to pointer to int when the reference is bound to it. Array decaying happens upon an lvalue to rvalue conversion. There is no such conversion when binding a reference to an object.


  1. what is the difference between a function having an array pointer paremeter void my_func (const T* array) and void my_func (const T(* array)[10])(with or without explicit array size) new version?

const T* is a pointer to const T.

const T(*)[10] is pointer to const T[10] i.e. pointer to an array of 10 T. For example, if T is an alias of int, then former is const int* and latter is const int(*)[10].

Note that if T is an array, then the former can also be a pointer to an array. For example if T is int[42], then former is const int(*)[42] i.e. a pointer to an array of 42 const int while the latter is const int(*)[42][10] i.e. pointer to an array of 10 arrays of 42 const int.

... and void my_func (const T* array[10])(with or without explicit array size) old version?

const T* array[10] is an array of 10 pointers to const T. However, a function parameter is never an array in C++. If a function is declared with an array parameter, that parameter is adjusted to be a pointer to element of such array. The element of an array of 10 pointers to const T is a pointer to const T i.e. const T* and a pointer to such object is const T**. As such, this parameter is actually const T**.

The difference between const T* and const T** is that latter is a pointer to the former.

If this works, then is the general template for any array size as below unnecessary?

It depends. If you don't need it, then it is unnecessary for your use case.

But that doesn't mean that it would be unnecessary altogether. For example, if you need a function overload that accepts only arrays, then you cannot use a T& parameter which can accept arguments that are not arrays.

why does template<typename _Ret, typename _Coll> requires 2 template arguments

Because the S in T (&arr)[S] has to be a template value parameter in order for the reference to be bound to arrays of any size. If you used a non-variable constant such as T (&arr)[10], then you could only accept arrays of one size.

but Sum<int64_t>(arr) specifies only 1 template argument?

This one doesn't use a reference to an array of S elements, so there is no need for a template value parameter S.


_Ret and _Coll names are reserved to the language implementation. By defining those names as template parameters, the behaviour of the program will be undefined. Do not define reserved names. You should choose other names for the template parameters.

3 Comments

yes, for the 2nd question, I edited my mistake. So const T* array[10] is now const T(* array)[10], which is a pointer to an array, which was my intention.
For your answer to my 1st question, I understand that alias of int [10] will conveniently do the job. But the example in my question neither defines alias nor passes array size. Function calling statement simply passes arr which is converted(decayed) into a pointer to int.
@Sean In short: Yes, you do have a type alias and no, the array does not decay. See my edited answer for more details.

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.