C ++ 14 будет иметь функции, чей тип возврата может быть выведен на основе возвращаемого значения.
auto function(){
return "hello world";
}
Могу ли я применить это поведение к функциям, которые используют enable_if для СФИНА по типу возвращаемого значения?
Например, давайте рассмотрим следующие две функции:
#include <type_traits>
#include <iostream>
//This function is chosen when an integral type is passed in
template<class T >
auto function(T t) -> typename std::enable_if<std::is_integral<T>::value>::type {
std::cout << "integral" << std::endl;
return;
}
//This function is chosen when a floating point type is passed in
template<class T >
auto function(T t) -> typename std::enable_if<std::is_floating_point<T>::value>::type{
std::cout << "floating" << std::endl;
return;
}
int main(){
function(1); //prints "integral"function(3.14); //prints "floating"
}
Как видите, правильная функция выбирается с помощью SFINAE по типу возвращаемого значения.
Тем не менее, это обе пустые функции. Второй параметр enable_if
по умолчанию установлено void
, Это было бы то же самое:
//This function is chosen when an integral type is passed in
template<class T >
auto function(T t) -> typename std::enable_if<std::is_integral<T>::value, void>::type {
std::cout << "integral" << std::endl;
return;
}
//This function is chosen when a floating point type is passed in
template<class T >
auto function(T t) -> typename std::enable_if<std::is_floating_point<T>::value, void>::type{
std::cout << "floating" << std::endl;
return;
}
Есть ли что-то, что я могу сделать с этими двумя функциями, чтобы их возвращаемый тип определялся по возвращаемому значению?
GCC 4.8.2 (используя --std=c++1y
)
std::enable_if
необязательно указывать возвращаемый тип, так как в C ++ 11 он может быть частью параметров шаблона.
Таким образом, ваши эквивалентные функции могут быть (или, ну, что-то в этом роде):
enum class enabler_t {};
template<typename T>
using EnableIf = typename std::enable_if<T::value, enabler_t>::type;
//This function is chosen when an integral type is passed in
template<class T, EnableIf<std::is_integral<T>>...>
auto function(T t) {
std::cout << "integral" << std::endl;
return;
}
//This function is chosen when a floating point type is passed in
template<class T, EnableIf<std::is_floating_point<T>>...>
auto function(T t) {
std::cout << "floating" << std::endl;
return;
}
Это также может быть параметр в функции:
//This function is chosen when an integral type is passed in
template<class T>
auto function(T t, EnableIf<std::is_integral<T>>* = nullptr) {
std::cout << "integral" << std::endl;
return;
}
//This function is chosen when a floating point type is passed in
template<class T>
auto function(T t, EnableIf<std::is_floating_point<T>>* = nullptr) {
std::cout << "floating" << std::endl;
return;
}
Это сохранит автоматическое удержание типа и SFINAE.
std::enable_if
может быть типом возврата, параметром функции или параметром шаблона. Вы получите ошибку переопределения функции, если вы используете тип возвращаемого значения или параметр шаблона, поэтому вам нужно использовать std::enable_if
в качестве параметра функции:
#include <type_traits>
#include <iostream>
template<class T, typename = typename std::enable_if<std::is_integral<T>::value, void>::type>
auto function(T t, typename std::enable_if<std::is_integral<T>::value, void>::type* dummy = nullptr) {
std::cout << "integral" << std::endl;
return 0;
}
//This function is chosen when a floating point type is passed in
template<class T, typename = typename std::enable_if<std::is_floating_point<T>::value, void>::type>
auto function(T t, typename std::enable_if<std::is_floating_point<T>::value, void>::type* dummy = nullptr) {
std::cout << "floating" << std::endl;
return 0.0f;
}
int main()
{
auto ret = function(0); // integral
auto ret2 = function(0.0f); // floating
std::cout << std::boolalpha << std::is_integral<decltype(ret)>::value << std::endl; // true
std::cout << std::is_floating_point<decltype(ret2)>::value << std::endl; // true
}
В строке ответа @ user1508519 мы можем удалить параметр enable_if из метода и сохранить его только в качестве параметра шаблона.
Мы полагаемся на то, что enable_if<false>
не определяет type
, так enable_if<false>::type
, который не существует, является хорошим инструментом для SFINAE — в сигнатуре метода, которая включает параметры шаблона.
Нет необходимости использовать этот параметр шаблона в самом методе!
Таким образом:
template<class T,
typename std::enable_if<std::is_integral<T>::value>::type* dummy = nullptr>
auto function(T t) {
std::cout << "integral" << std::endl;
return 0;
}
// This function is chosen when a floating point type is passed in
template<class T,
typename std::enable_if<std::is_floating_point<T>::value>::type* dummy = nullptr>
auto function(T t) {
std::cout << "floating" << std::endl;
return 0.0f;
}
int main() {
auto ret = function(0); // integral
auto ret2 = function(0.0f); // floating
cout << std::boolalpha;
cout << std::is_integral<decltype(ret)>::value << endl; // true
cout << std::is_floating_point<decltype(ret2)>::value << endl; // true
}
integral
floating
true
true
Как уже упоминалось в другом месте std::enable_if
может использоваться для формирования любого типа возврата; параметр функции; или параметр шаблона.
Однако последние два метода имеют недостаток в том, что они изменяют сигнатуру соответствующей функции или объекта. С помощью std::enable_if
с другой стороны, при возвращаемом типе количество параметров функции и шаблона остается неизменным.
С автоматическим выводом лямбда-типов возврата в C ++ 11 (вероятно, распространяемых на обычные функции в C ++ 14) было бы идеально, если бы существовал метод, позволяющий выводить оба типа возврата; а также использование std::enable_if
на тип возврата. Чтобы съесть свой торт и съесть его — почти. Увы, кажется, что в настоящее время это невозможно.