I'm looking to pass an array of structs to a function and wanted to check that the way I'm doing it and using internally within the function is good practice and valid.
I don't think passing c-style arrays by const reference is necessarily an improvement. It makes it impossible to call the function if all you've got is a pointer to the first element and a length, or if you are not using all elements in the array (maybe you only want to pass a sub-portion of the array). Making the length of the array a template argument also leads to a lot of code duplication (assuming you pass arrays of many different lengths) which might not be optimal from a code size perspective.
C++20 introduces std::span which is essentially a wrapper around a pointer and a length. This makes passing arrays and other contigous containers (e.g. std::vector) easier and less error prone.
If you're not using C++20 yet you could of course implement your own span type, or use a library such as Boost, but it might not be worth the trouble. Your original code uses a tried-and-true approach. It might look feel a bit more like "C" but there is nothing necessarily wrong in using it in C++.
The traditional C++ approach has otherwise been to write such "functions" using iterators. This has the added benefit that you can even use it on non-contiguous containers such as std::deque or std::list.
Thanks for your thoughts! Interesting read. I've only got a basic level of c coding knowledge which is why I thought I'd pass a reference to an array, but I'll look into the more modern ways of doing this as you've suggested.
I was wondering though (out of interest) is it still valid to access elements of the array using the . notation i.e.param[i].value2 even though I've passed in the array using a pointer? I wanted to check i'm not using the syntax improperly - even though as you've suggested there are more modern ways of doing this.
If, however, in the OP param comes from passing a pointer from say new or std::unique_ptr, then you do need to pass the number of elements as this cannot be established and in this case std::s[an won't wotk as it cannot determine begin/end of the span as is required.
The C syntax rules (which was later inherited by C++) was intentionally designed to be able to use arrays and pointers to the first element in arrays the same way.
1 2 3 4
int arr[5]; // array of 5 ints
int* p = arr;// pointer to first element in arr. Equivalent to `int* p = &arr[0];`
arr[0] = 1; // set the first array element to 1.
p[1] = 2; // set the second array element to 2.
When declaring a function that takes an array as argument (without using references) you're actually declaring a function that takes a pointer as argument.
The three following function declarations means exactly the same (i.e. a function named foo that takes an int* as argument).
This has sometimes lead beginners to think that arrays are just (const) pointers. Some teachers even tell them so. But it's not true and eventually you will run into situations where you need to know the difference. A very common mistake beginners make is when they try to use sizeof to figure out the length of an array without realizing they have a pointer and not an array so the calculated length becomes incorrect. A safer alternative is to use std::size, which also cannot calculate the array length from pointers, but if you try you'll get a compilation error which is much better than an incorrect length at runtime.