Я имею в виду что-то вроде:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Я имею в виду что-то вроде:
int main()
{
void a()
{
// code
}
a();
return 0;
}
В текущих версиях C++ (C++ 11, C++ 14 и C++ 17) у вас могут быть функции внутри функций в форме лямбды:
int main() {
// This declares a lambda, which can be called just like a function
auto print_message = [](std::string message)
{
std::cout << message << "\n";
};
// Prints "Hello!" 10 times
for(int i = 0; i < 10; i++) {
print_message("Hello!");
}
}
Лямбда также может изменять локальные переменные посредством ** захвата по ссылке *. С помощью захвата по ссылке лямбда имеет доступ ко всем локальным переменным, объявленным в области лямбда. Он может изменять и изменять их в обычном режиме.
int main() {
int i = 0;
// Captures i by reference; increments it by one
auto addOne = [&] () {
i++;
};
while(i < 10) {
addOne(); //Add 1 to i
std::cout << i << "\n";
}
}
C++ не поддерживает это напрямую.
Тем не менее, у вас могут быть локальные классы, и у них могут быть функции (non- static
или static
), так что вы можете получить это в некоторой степени, хотя это будет немного клуджем:
int main() // it int, dammit!
{
struct X { // struct as good as class
static void a()
{
}
};
X::a();
return 0;
}
Однако я бы поставил под сомнение практику. Все знают (хорошо, теперь, когда вы это делаете, в любом случае :)
) C++ не поддерживает локальные функции, поэтому они привыкли их не иметь. Однако они не используются для этого клуджа. Я потратил бы довольно много времени на этот код, чтобы убедиться, что он действительно только для локальных функций. Нехорошо.
Для всех намерений и целей C++ поддерживает это через лямбды: 1
int main() {
auto f = []() { return 42; };
std::cout << "f() = " << f() << std::endl;
}
Здесь f
является лямбда-объектом, который действует как локальная функция в main
. Захваты могут быть определены, чтобы позволить функции получить доступ к локальным объектам.
За кулисами f
- это функциональный объект (т.е. Объект типа, который предоставляет operator()
). Тип объекта функции создается компилятором на основе лямбды.
1, так как C++ 11
Локальные классы уже упомянуты, но вот способ позволить им проявиться еще больше как локальные функции, используя перегрузку operator() и анонимный класс:
int main() {
struct {
unsigned int operator() (unsigned int val) const {
return val<=1 ? 1 : val*(*this)(val-1);
}
} fac;
std::cout << fac(5) << '\n';
}
Я не советую использовать это, это просто забавный трюк (может сделать, но imho не должен).
С появлением С++ 11 назад вы можете теперь иметь локальные функции, синтаксис которых немного напоминает JavaScript:
auto fac = [] (unsigned int val) {
return val*42;
};
Нет.
Что вы пытаетесь сделать?
обходной путь:
int main(void)
{
struct foo
{
void operator()() { int a = 1; }
};
foo b;
b(); // call the operator()
}
Старый ответ: Вы можете, вроде как, но вы должны обмануть и использовать фиктивный класс:
void moo()
{
class dummy
{
public:
static void a() { printf("I'm in a!\n"); }
};
dummy::a();
dummy::a();
}
Более новый ответ: более новые версии C++ также поддерживают лямбды, чтобы сделать это лучше/правильно. Смотрите ответы выше на странице.
Нет, это не разрешено. Ни С++, ни С++ не поддерживают эту функцию по умолчанию, однако TonyK указывает (в комментариях), что есть расширения для компилятора GNU C, которые активируют это поведение в C.
Вы не можете определить свободную функцию внутри другого в С++.
Как уже упоминалось, вы можете использовать вложенные функции, используя расширения gnu-языка в gcc. Если вы (или ваш проект) привязаны к gcc toolchain, ваш код будет в основном переносимым в разных архитектурах, нацеленных на компилятор gcc.
Однако, если есть возможное требование, что вам может понадобиться скомпилировать код с другой инструментальной цепочкой, я бы держался подальше от таких расширений.
Я также осторожно обращаюсь с осторожностью при использовании вложенных функций. Они являются прекрасным решением для управления структурой сложных, но сплоченных блоков кода (фрагменты которых не предназначены для внешнего/общего использования). Они также очень полезны в борьбе с загрязнением пространства имен (очень актуальная проблема с естественно сложными/длинные классы в подробных языках.)
Но, как и все, они могут быть открыты для злоупотреблений.
Печально, что C/С++ не поддерживает такие функции, как стандарт. Большинство вариантов pascal и Ada do (почти все языки на Алголе). То же самое с JavaScript. То же самое с современными языками, такими как Scala. То же самое с почтенными языками, такими как Erlang, Lisp или Python.
И так же, как и с C/С++, к сожалению, Java (с которой я зарабатываю большую часть своей жизни) не делает.
Я упоминаю Java здесь, потому что вижу несколько плакатов, предлагающих использование классов и методов класса в качестве альтернативы вложенным функциям. И это также типичный обходной путь в Java.
Короткий ответ: Нет.
Это приводит к искусственной, ненужной сложности иерархии классов. При равных условиях идеальным является наличие иерархии классов (и ее охватывающих пространств имен и областей), представляющих фактический домен как можно более простой.
Вложенные функции помогают справиться с "private", сложностью внутри функции. Не имея этих средств, следует попытаться избежать распространения этой сложности "private" и в одну модель класса.
В программном обеспечении (и в любой инженерной дисциплине) моделирование - это вопрос компромиссов. Таким образом, в реальной жизни будут оправданы исключения из этих правил (или, скорее, рекомендации). Однако действуйте осторожно.
Все эти трюки просто выглядят (более или менее) как локальные функции, но они не работают так. В локальной функции вы можете использовать локальные переменные суперфункций. Это своего рода полу-глобалы. Без этих трюков это можно сделать. Самый близкий лямбда-трюк от С++ 0x, но его закрытие связано во время определения, а не время использования.
Вы не можете иметь локальные функции в C++. Однако в C++ 11 есть лямбды. Лямбды - это переменные, которые работают как функции.
Лямбда имеет тип std::function
(на самом деле это не совсем верно, но в большинстве случаев можно предположить, что это так). Чтобы использовать этот тип, вам нужно #include <functional>
. std::function
является шаблоном, принимающим в качестве аргумента шаблона тип возвращаемого значения и типы аргумента, с синтаксисом std::function<ReturnType(ArgumentTypes)
. Например, std::function<int(std::string, float)>
- это лямбда-выражение, возвращающее int
и принимающее два аргумента, один std::string
и один float
. Наиболее распространенным является std::function<void()>
, который ничего не возвращает и не принимает аргументов.
Как только лямбда объявлена, она вызывается так же, как и обычная функция, используя синтаксис lambda(arguments)
.
Чтобы определить лямбду, используйте синтаксис [captures](arguments){code}
(есть другие способы сделать это, но я не буду здесь их упоминать). arguments
- это аргументы, которые принимает лямбда, а code
- это код, который должен выполняться при вызове лямбды. Обычно вы помещаете [=]
или [&]
качестве снимков. [=]
означает, что вы перехватываете все переменные в области, в которой значение определяется значением, что означает, что они сохранят значение, которое они имели при объявлении лямбды. [&]
означает, что вы захватываете все переменные в области видимости по ссылке, что означает, что они всегда будут иметь свое текущее значение, но если они будут удалены из памяти, программа потерпит крах. Вот некоторые примеры:
#include <functional>
#include <iostream>
int main(){
int x = 1;
std::function<void()> lambda1 = [=](){
std::cout << x << std::endl;
};
std::function<void()> lambda2 = [&](){
std::cout << x << std::endl;
};
x = 2;
lambda1(); //Prints 1 since that was the value of x when it was captured and x was captured by value with [=]
lambda2(); //Prints 2 since that the current value of x and x was captured by value with [&]
std::function<void()> lambda3 = [](){}, lambda4 = [](){}; //I prefer to initialize these since calling an uninitialized lambda is undefined behavior.
//[](){} is the empty lambda.
{
int y = 3; //y will be deleted from the memory at the end of this scope
lambda3 = [=](){
std::cout << y << endl;
};
lambda4 = [&](){
std::cout << y << endl;
};
}
lambda3(); //Prints 3, since that the value y had when it was captured
lambda4(); //Causes the program to crash, since y was captured by reference and y doesn't exist anymore.
//This is a bit like if you had a pointer to y which now points nowhere because y has been deleted from the memory.
//This is why you should be careful when capturing by reference.
return 0;
}
Вы также можете захватить определенные переменные, указав их имена. Просто указав их имя, вы поймете их по значению, указав их имя с &
перед тем, как захватите их по ссылке. Например, [=, &foo]
будет захватывать все переменные по значению, кроме foo
который будет захвачен по ссылке, а [&, foo]
захватит все переменные по ссылке, кроме foo
который будет захвачен по значению. Вы также можете захватывать только определенные переменные, например, [&foo]
захватывает foo
по ссылке и не захватывает другие переменные. Вы также можете перехватить переменные вообще без использования []
. Если вы попытаетесь использовать переменную в лямбде, которую вы не захватили, она не скомпилируется. Вот пример:
#include <functional>
int main(){
int x = 4, y = 5;
std::function<void(int)> myLambda = [y](int z){
int xSquare = x * x; //Compiler error because x wasn't captured
int ySquare = y * y; //OK because y was captured
int zSquare = z * z; //OK because z is an argument of the lambda
};
return 0;
}
Вы не можете изменить значение переменной, которая была захвачена значением внутри лямбды (переменные, захваченные значением, имеют тип const
внутри лямбды). Для этого вам нужно захватить переменную по ссылке. Вот пример:
#include <functional>
int main(){
int x = 3, y = 5;
std::function<void()> myLambda = [x, &y](){
x = 2; //Compiler error because x is captured by value and so it of type const int inside the lambda
y = 2; //OK because y is captured by reference
};
x = 2; //This is of course OK because we're not inside the lambda
return 0;
}
Кроме того, вызов неинициализированных лямбда-выражений является неопределенным поведением и обычно приводит к сбою программы. Например, никогда не делайте так:
std::function<void()> lambda;
lambda(); //Undefined behavior because lambda is uninitialized
Примеры
Вот код для того, что вы хотели сделать в своем вопросе, используя лямбды:
#include <functional> //Don't forget this, otherwise you won't be able to use the std::function type
int main(){
std::function<void()> a = [](){
// code
}
a();
return 0;
}
Вот более продвинутый пример лямбды:
#include <functional> //For std::function
#include <iostream> //For std::cout
int main(){
int x = 4;
std::function<float(int)> divideByX = [x](int y){
return (float)y / (float)x; //x is a captured variable, y is an argument
}
std::cout << divideByX(3) << std::endl; //Prints 0.75
return 0;
}
Позвольте мне разместить здесь решение для С++ 03, которое я считаю максимально возможным. *
#define DECLARE_LAMBDA(NAME, RETURN_TYPE, FUNCTION) \
struct { RETURN_TYPE operator () FUNCTION } NAME;
...
int main(){
DECLARE_LAMBDA(demoLambda, void, (){ cout<<"I'm a lambda!"<<endl; });
demoLambda();
DECLARE_LAMBDA(plus, int, (int i, int j){
return i+j;
});
cout << "plus(1,2)=" << plus(1,2) << endl;
return 0;
}
(*) в мире С++ с использованием макросов никогда не считается чистым.
Но мы можем объявить функцию внутри main():
int main()
{
void a();
}
Несмотря на то, что синтаксис верен, иногда это может привести к "наиболее неприятному анализу":
#include <iostream>
struct U
{
U() : val(0) {}
U(int val) : val(val) {}
int val;
};
struct V
{
V(U a, U b)
{
std::cout << "V(" << a.val << ", " << b.val << ");\n";
}
~V()
{
std::cout << "~V();\n";
}
};
int main()
{
int five = 5;
V v(U(five), U());
}
= > нет выхода программы.
(Только предупреждение Clang после компиляции).
Когда вы пытаетесь реализовать функцию в другом теле функции, вы должны получить эту error
как недопустимое определение:
error C2601: 'a' : local function definitions are illegal
IntelliSense: expected a ';'
Так что не пытайтесь снова.