c++arraystemplatestemplate-argument-deductionperfect-forwarding

How to make a forwarding reference for arrays of items?


I am learning right now about generic programming in C++ and I am trying to figure out how to forward reference an array of items.

I have managed to do perfect forwarding for regular items:

template<typename T>
void do_something(T &&item) {...}

And I have managed to pass an array reference to a template function:

template<typename T, unsigned int L>
void do_something(T (&arr)[L]) {...}

int main()
{
  int array[] = {1,2,3};
  do_something(array);
}

// works perfectly fine

And I can also pass an array as an rvalue:

template<typename T, unsigned int L>
void do_something(T (&&arr)[L]) {...}

int main()
{
  do_something({1,2,3});
}

// also works

The compiler can deduce the types for all of those examples. I thought that the last function I have provided could accept both an rvalue and an lvalue (as done by the first one), but the compiler raises an error when it is provided with lvalues.

Is there any way of making a single template function such that it can accept both rvalue and lvalues of arrays without overloading it? (While the compiler can still deduce the types)


Solution

  • In you case, if you don't mutate content, you can take by const reference, as temporary can bind to it:

    template <typename T, st::size_t N>
    void do_something(const T (&arr)[N]) {/*...*/}
    

    Else, you might use template<typename T> void do_something(T&& item) and dispatch inside it:

    template<typename T> void do_something(T&& item)
    {
        if constexpr(std::is_array_v<std::remove_reference_t<T>>) {
            constexpr std::size_t N = std::extend_v<std::remove_reference_t<T>>;
            using Extent = std::remove_extent_t<std::remove_reference_t<T>>;
            // std::remove_reference_t<T> is Extent[N]
            constexpr bool is_lvalue_ref = std::is_lvalue_reference_v<T&&>;
            // ...
        }
    }