Получить тип возвращаемого начала в массиве c

Я хотел бы получить тип возврата std::begin общим способом. Мое текущее решение:

using type = decltype(std::begin(std::declval<T>()));

и работает, когда T = std::vector<int>. Но я не понимаю, почему следующее не работает:

using type = decltype(std::begin(std::declval<int[3]>()));

Я получаю сообщение об ошибке:

example.cpp:83:60: error: no matching function for call to ‘begin(int [3])’
     using type = decltype(std::begin(std::declval<int[3]>()));

Как получить возвращаемый тип std::begin общим способом?

Ответ 1

Перегрузка для массивов:

template< class T, std::size_t N > 
constexpr T* begin( T (&array)[N] );

И std::declval<int[3]>() дает вам int(&&)[3], который не соответствует этой перегрузке. Он также не соответствует нормальной перегрузке контейнера, потому что это SFINAE-ed при наличии c.begin(). Таким образом, у вас нет соответствующей функции.

Вместо этого вам нужно передать ссылку lvalue на массив на begin(), чтобы вернуть итератор. Таким образом, либо вам нужно вручную указать ссылку на lvalue, когда вы используете свой псевдоним:

template <class T>
using type = decltype(std::begin(std::declval<T>()));

using arr = type<int(&)[3]>; // int*

или сам псевдоним предоставляет ссылку lvalue для вас:

template <class T>
using type = decltype(std::begin(std::declval<T&>()));

using arr = type<int[3]>; // int*

Первое кажется мне более правильным, но YMMV.