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.
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);