Почему мы не можем автоматически выводить возвращаемые типы?
Недавно я работал с другом, который хотел сделать С++ более Haskell-y, и нам нужна функция, которая в основном выглядит так:
auto sum(auto a, auto b) {
return a + b;
}
По-видимому, я не могу использовать auto как тип параметра, поэтому я изменил его на это:
template<class A, class B>
auto sum(A a, B b) {
return a + b;
}
Но это тоже не работает. Что мы в конце концов поняли, нам нужно это:
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b) {
return a + b;
}
Итак, мой вопрос в том, какой смысл? Разве не decltype
просто повторять информацию, так как компилятор может просто посмотреть на оператор return?
Я подумал, что, возможно, это необходимо, поэтому мы можем просто включить заголовочный файл:
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b);
... но мы все равно не можем использовать такие шаблоны.
Другая вещь, которую я рассматривал, заключалась в том, что компилятор может быть проще, но похоже, что на самом деле это будет сложнее.
Случай 1: с decltype
- Выясните тип оператора
decltype
- Выясните типы возвращаемых значений
- Посмотрите, соответствуют ли они
Случай 2: Без decltype
- Выясните типы возвращаемых значений
- Посмотрите, соответствуют ли они
Итак, с учетом этих вещей, какая точка возвращаемого возвращаемого типа с decltype
?
Ответы
Ответ 1
Что делать, если у нас есть следующее:
template<class A, class B, class C>
auto sum(A a, B b, C c) {
if (rand () == 0) return a + b;
// do something else...
return a + c;
}
.. где выражения a + b
и a + c
дают разные типы результатов.
Что должен компилятор решить поставить как возвращаемый тип для этой функции и почему?
Этот случай уже рассматривается С++ 11 lambdas, которые позволяют опустить возвращаемый тип, если операторы return
могут быть выведены одному типу (требуется стандартная цитата из NB), некоторые источники утверждают, что разрешено только одно возвращаемое выражение, и что это является gcc-глюком).
Техническая причина состоит в том, что С++ позволяет определять и декларировать отдельные.
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b);
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b)
{
}
Определение шаблона может быть в заголовке. Или это может быть в другом файле, так что вам не придется пробираться через страницы и страницы определений функций при просмотре интерфейса.
С++ должен учитывать все возможности. Ограничение возвращаемых типов возвращаемых значений только определением функций означает, что вы не можете сделать что-то простое:
template<class A, class B>
class Foo
{
auto sum(A a, B b) -> decltype(a + b);
}
template<class A, class B>
auto Foo<A, B>::sum(A a, B b) -> decltype(a + b)
{
}
Ответ 2
но мы все равно не можем использовать такие шаблоны.
Во-первых, возвращаемые возвращаемые типы не являются просто шаблоном. Они работают для всех функций. Во-вторых, говорит кто? Это совершенно законный код:
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b);
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b)
{
}
Определение шаблона может быть в заголовке. Или это может быть в другом файле, так что вам не придется пробираться через страницы и страницы определений функций при просмотре интерфейса.
С++ должен учитывать все возможности. Ограничение возвращаемых типов возвращаемых значений только определением функций означает, что вы не можете сделать что-то простое:
template<class A, class B>
class Foo
{
auto sum(A a, B b) -> decltype(a + b);
}
template<class A, class B>
auto Foo<A, B>::sum(A a, B b) -> decltype(a + b)
{
}
И это довольно часто для многих программистов. Нет ничего плохого в том, что вы хотите кодировать этот путь.
Единственная причина, по которой lambdas уйти без возвращаемого типа, состоит в том, что они должны иметь тело функции, определенное с определением. Если вы ограничили типы возвращаемых возвратов только теми функциями, в которых было определено определение, вы не сможете использовать любой из перечисленных случаев.
Ответ 3
Нет никаких технических причин, почему это невозможно. Основная причина, по которой они не связаны, заключается в том, что язык С++ движется очень медленно, и для добавления функций требуется очень много времени.
Вы можете получить хороший синтаксис с lambdas (но вы не можете иметь templacized аргументы в lambdas, опять же без уважительной причины).
auto foo = [](int a, double b)
{
return a + b;
};
Да, есть случаи, когда тип возврата не может быть автоматически выведен. Точно так же, как в лямбда, это может быть просто требованием объявить тип возврата в этих двусмысленных случаях.
В настоящий момент ограничения настолько произвольны, что их сложно расстраивать. Также см. Удаление понятий для добавления разочарования.
Ответ 4
В блоге от Dave Abrahams он обсуждает предложение о наличии этого синтаксиса функции:
[]min(x, y)
{ return x < y ? x : y }
Это основано на возможном предложении для полиморфных лямбда. Он также начал работу здесь об обновлении clang для поддержки этого синтаксиса.
Ответ 5
Маленькая надежда уговорить комитет добавить такую языковую функцию, хотя никаких технических препятствий нет. Но, возможно, можно убедить группу GCC добавить расширение компилятора.