1
#include <iostream>


/* prints sequentially the elements of an array */
template<typename T, unsigned int _size>
void log_seq_pbr(const T (&x)[_size]){

    for(unsigned int i = 0; i < _size ; i++){
        std::cout << i << " " << x[i] << std::endl;
    }

    return;
}

/* prints sequentially the elements of an array */
template<typename T, unsigned int _size>
void log_seq_pbv(const T x[_size]){

    for(unsigned int i = 0; i < _size ; i++){
        std::cout << i << " " << x[i] << std::endl;
    }

    return;
}


int main(int argc, char const *argv[])
{
    
    int arr[10] = {0};

    log_seq_pbr(arr);
    log_seq_pbv(arr);


    return 0;
}

The only difference between those two funtions is .._pbr takes its argument by reference, while .._pbv takes its argument by value. The call to by reference one compiles fine, however, the call to by value one gives the following error:

tmp.cpp: In function 'int main(int, const char**)':        
tmp.cpp:33:20: error: no matching function for call to 'log_seq_pbv(int [10])'
   33 |     log_seq_pbv(arr);
      |                    ^
tmp.cpp:17:6: note: candidate: 'template<class T, unsigned 
int _size> void log_seq_pbv(const T*)'
   17 | void log_seq_pbv(const T x[_size]){
      |      ^~~~~~~~~~~
tmp.cpp:17:6: note:   template argument deduction/substitution failed:
tmp.cpp:33:20: note:   couldn't deduce template parameter '_size'
   33 |     log_seq_pbv(arr);

Why it cannot figure out _size for pass-by-value, while it is able to do it for pass-by-reference one?

muyustan
  • 1,555
  • 1
  • 11
  • 23
  • 3
    Templates are a red-herring, you cannot pass arrays by value. See [Why can't we pass arrays to function by value?](https://stackoverflow.com/questions/7454990/why-cant-we-pass-arrays-to-function-by-value) – kmdreko Dec 04 '20 at 21:12
  • 1
    Note the signature of `log_seq_pbv()` in the error message: `void log_seq_pbv(const T*)`. In a function parameter, `const T x[_size]` is just syntax sugar for `const T x[]` (the `_size` is ignored), which in turn is just sugar for `const T* x`. A simple pointer is being passed, any size information is lost, hence why the `_size` template argument can't be deduced. [You can't pass arrays around by value](https://stackoverflow.com/questions/7454990/). If you need to do that, use `std::array` instead. – Remy Lebeau Dec 04 '20 at 21:17

1 Answers1

1

In log_seq_pbv you're trying to get the template to automatically deduce the size of the C-style array x and define a different function in each case. This is not going to work, because C-style arrays don't know their own sizes. You need to pass the size of the array as an actual argument to the function.

In log_seq_pbr the syntax T (&x)[_size] is a very special syntax that is used specifically for declaring references to arrays of a known size (see here for more details). It's the only way to do what you're trying to do here with C-style arrays, because it gives the compiler the information it needs to choose the correct template parameter.

However, it would be much better to use the C++ std::array container instead here, since the whole point of that container is that it's an array that knows its own size. Here's an example:

#include <array>
#include <iostream>

using namespace std;

template <typename T, size_t s>
void print_elements(const array<T, s> &a)
{
    for (size_t i = 0; i < s; i++)
        cout << i << ": " << a[i] << '\n';
    cout << '\n';
}

int main()
{
    array<int, 10> arr{0};
    print_elements(arr);
}

Also, you can't pass C-style arrays by value. Arrays automatically "decay" into pointers when you pass them to functions. x[0] is essentially nothing more than an alias of *x, i.e. accessing the element pointed to by the pointer (a.k.a. dereferencing the pointer) and x[n] is an alias of *(x + n), i.e. accessing the element stored at the address n elements after the first one.

Finally, there's usually no reason to pass an array to a function by value, since that means the entire array would need to be copied, and this would be time-consuming, especially for large arrays. You may need to pass an array by value if you specifically want the function to make changes to the copy of the array without it affecting the original array you passed to it. This can be achieved using std::array, but since your function only prints out the elements of the array, there's no reason to pass it by value in the first place.