c++c++17sfinaeforwarding-reference

Function template correct definition for forward reference to an array and type of element


Minimal working example:

#include <array>
#include <iostream>

template <typename T> struct is_array : std::false_type {};
template <typename U, std::size_t N> struct is_array<std::array<U, N>> : std::true_type {};

template <typename T>
std::enable_if_t<is_array<std::decay_t<T>>::value>
do_something_prob(T&& arr, typename T::value_type mul)  // `arr` becomes an rvalue?
{
    for (auto && v : arr)
        std::cout << v * mul << '\n';
}

template <
    typename T, 
    typename ElemT = std::enable_if_t<is_array<std::decay_t<T>>::value, typename T::value_type>
>
void do_something_no_prob(T&& arr, ElemT mul)
{
    for (auto && v : arr)
        std::cout << v * mul << '\n';
}

int main()
{
    auto arr = std::array<int, 3>{1, 2, 3};
    
    do_something_prob(std::array<int, 3>{1, 2, 3}, 2);
    // do_something_prob(arr, 2);  // error message: 'std::array<int, 3>&' is not a class, struct, or union type'

    do_something_no_prob(std::array<int, 3>{1, 2, 3}, 2);
    do_something_no_prob(arr, 2);
}

What am I trying to achieve: implement a function template that accepts two arguments, (1) a forward reference to any array type and (2) a copy of something of the same type as the array's elements.

This is achieved with do_something_no_prob. But what is the problem with do_something_prob? It can't handle lvalues.


Solution

  • References do not have member aliases. Its the same problem as in

    #include <array>
    int main()
    {
        using arr_t = std::array<int,42>&;
        using elem_broken = arr_t::value_type;          // error
        using elem_t = std::decay_t<arr_t>::value_type; // ok
    }
    

    You use std::decay_t for SFINAE, but then miss to use it when querying the member alias.

    template <typename T>
    std::enable_if_t<is_array<std::decay_t<T>>::value>
    do_something_prob(T&& arr, typename std::decay_t<T>::value_type mul); 
    

    Complete Demo