Выведенные типы возврата не относятся к SFINAE. Эта перегрузка просто выпадет из набора перегрузки, если t[i]
недопустимо:
template<typename T>
auto access(T& t, int i)
-> decltype(t[i])
{
return t[i];
}
Принимая во внимание, что эта перегрузка не произойдет, что приведет к серьезной ошибке:
template<typename T>
decltype(auto) access(T& t, int i)
{
return t[i];
}
Демо
Кроме того, вы можете столкнуться с проблемами с конфликтующими выведенными типами возврата. Подумай, хочу ли я вернуть std::optional<T>
. Следующий код не компилируется, поскольку std::nullopt_t
отличается от типа std::optional<T>
:
#include <optional> // C++17 standard library feature
template <typename T>
auto foo(T const& val)
{
if (val.is_invalid()) return std::nullopt;
return val.some_function_returning_an_optional();
}
Завершающие типы возвращаемых значений позволяют точно указать тип возвращаемых выражений:
template <typename T>
auto foo(T const& val)
-> decltype(val.some_function_returning_an_optional())
{
if (val.is_invalid()) return std::nullopt;
return val.some_function_returning_an_optional();
}
Вы можете использовать ведущий тип возвращаемого значения, но это потребует использования std::declval
, что усложнит понимание:
template <typename T>
decltype(std::declval<T const&>().some_function_returning_an_optional())
foo(T const& val)
{
if (val.is_invalid()) return std::nullopt;
return val.some_function_returning_an_optional();
}
Демо