Недавно я столкнулся с кодом C++, который, по-видимому, объявлял локальную функцию auto
, и не знал, как именно ее интерпретировать.
Учитывая следующий код:
int test(int input) {
auto f(int) -> int;
return f(input);
}
Что именно здесь f
? При каких условиях этот код имеет смысл?
Это то же самое, что int f(int);
Т.е. обычное объявление функции.
Функция нуждается как в объявлении, так и в определении. То, что вы здесь показали, является декларацией, и пока где-то есть соответствующее определение, все будет в порядке.
Сторона, не являющаяся C++, не имеет локальных (вложенных) функций. Ближе всего к этому относятся лямбда-выражения.
@ChrisMM Да, у этой функции есть определение в другом месте. Однако я все еще немного не понимаю, зачем вам это делать внутри функции.
@Chuu Меня это тоже смущает. За более чем 30 лет существования C++ я не видел, чтобы это использовалось где-либо еще (возможно, это предотвращает необходимость в коде загружать полный файл заголовка)
Я все еще немного озадачен, почему вы хотите это сделать. Возможно, заголовочный файл, в котором объявлен f
, не существует, и программисту не хотелось создавать заголовок (возможно, быстро и грязно, или лень). ?
Я бы это проверил ^
Включите^HDОбъявите, что вы используете :-)
Может быть, в тестовой среде связаны или определены разные функции f(), и это общий вызывающий объект?
Это объявление функции f
(принимающей int
и возвращающей int
), которая делает ее доступной для вызова внутри test
.
Он использует стиль завершающего возвращаемого типа (доступен начиная с C++11).
Это похоже на более традиционную форму объявления:
int test(int input) {
int f(int); // <- declaration for f
return f(input);
}
Это позволяет вызывать f
изнутри test
, но не из него (при условии отсутствия глобального объявления). Но я не могу припомнить ни одного случая, когда бы это было действительно полезно.
Примечания:
Это всего лишь декларация. Вам нужно определение f
(с телом функции) в одной из единиц перевода, чтобы избежать ошибки компоновщика.
C++ не поддерживает вложенные локальные функции. Фактическая функция f
должна быть реализована вне test
, например:
int f(int n) {
// or: auto f(int n) -> int {
return n*2;
}
int test(int input) {
auto f(int) -> int;
return f(input);
}
f
также можно реализовать в другой единице перевода.
То, что вы видите, — это объявление функции (которое обычно записывается в заголовочном файле) с завершающим типом возвращаемого значения, которое поддерживается начиная с C++11. Поведение следующих двух строк кода идентично.
auto f(int) -> int;
int f(int);
cppreference для объявлений функций:
noptr-declarator
(parameter-list
)cv
(необязательно)ref
(необязательно)except
(необязательно)attr
(необязательно)
noptr-declarator
(parameter-list
)cv
(необязательно)ref
(необязательно)except
(необязательно)attr
(необязательно) ->trailing
Для второго варианта требуется ключевое слово auto
. В вашем случае нет никакой пользы в использовании конечного возвращаемого типа, кроме, возможно, более последовательного или предпочтительного стиля.
Когда тип возвращаемого значения более сложный или находится внутри другой области, вариант завершающего типа возвращаемого значения имеет некоторые преимущества. Давайте посмотрим на следующий пример:
struct Foo {
using T = int;
T bar();
};
// "normal" style
Foo::T Foo::bar() {
return 10;
}
// trailing return type, doesn't need "Foo::" twice
auto Foo::bar() -> T {
return 10;
}
// This doesn't work. What is 'T'?
T Foo::bar() {
return 10;
}
(Even if the last implementation would be legal, this would not compile since it defines the same thing three times.)
В данном случае разница все еще не так уж и велика, но, надеюсь, вы поняли, на что она способна и почему она может быть полезна. Обычно (если тип возвращаемого значения не слишком сложен) выбор стиля зависит от ваших личных предпочтений.
Это не «автоматическая функция». Как объяснили другие, он использует возврат поезда, но между следующими двумя объявлениями нет разницы.
auto f(int) -> int;
int f(int);
Это декларации, а не определения. Для вызова функции необходимо определение.
Почему это может быть полезно? Дело в том, что test
можно определить без определения f
и даже без объявления f
, потому что test
это предоставляет.
Отсутствие декларации может иметь множество причин. Например, f
можно объявить и определить после test
, как в этом примере:
int test(int input) {
auto f(int) -> int;
return f(input);
}
int f(int x) {
if (x) return test(x-1);
return 42;
}
int main() {
return test(1);
}
Декларацию можно было также разместить за пределами test
, и в этом случае ее в просторечии называют предварительной декларацией:
auto f(int) -> int;
int test(int input) {
return f(input);
}
Это объявление функции. Но у него нет тела, и это должно вызвать ошибку компоновщика. godbolt.org/z/8TWbxWejs Дано ли
f
определение где-нибудь еще?