Я маю на увазі щось на кшталт:
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 ++ не підтримує це безпосередньо.
Це означає, що ви можете мати місцеві класи, і вони можуть мати функції (не ) static
або static
, так що ви можете отримати це до деякого розширення, хоч це і є трохи хитрості:
int main() // it's int, dammit!
{
struct X { // struct's as good as class
static void a()
{
}
};
X::a();
return 0;
}
Однак я б поставив під сумнів практику. Усі знають (ну, тепер, коли ви це робите :)
) C ++ не підтримує локальні функції, тому вони звикли їх не мати. Однак вони не звикли до цієї хитрості. Я витратив би досить часу на цей код, щоб переконатися, що він дійсно існує лише для дозволу локальних функцій. Не добре.
int main()
іint main(int argc, char* argv[])
int main()
і int main(int argc, char* argv[])
повинен підтримуватися та інші можуть бути підтримані , але всі вони мають зворотний Int.
Для всіх цілей і цілей C ++ підтримує це через лямбдаси : 1
int main() {
auto f = []() { return 42; };
std::cout << "f() = " << f() << std::endl;
}
Тут f
знаходиться лямбда-об’єкт, який виконує роль локальної функції в main
. Захоплення можна вказати, щоб функція мала доступ до локальних об'єктів.
За лаштунками f
- це об'єкт функції (тобто об'єкт типу, який надає operator()
). Тип об'єкта функції створюється компілятором на основі лямбда.
1, оскільки C ++ 11
+1
від мене.
Про місцеві класи вже згадувалося, але ось спосіб дозволити їм виглядати ще більше як локальні функції, використовуючи перевантаження оператора () та анонімний клас:
int main() {
struct {
unsigned int operator() (unsigned int val) const {
return val<=1 ? 1 : val*(*this)(val-1);
}
} fac;
std::cout << fac(5) << '\n';
}
Я не раджу використовувати це, це просто смішний трюк (можна, але імхо не повинен).
З часом підйом C ++ 11 тепер ви можете мати локальні функції, синтаксис яких трохи нагадує JavaScript:
auto fac = [] (unsigned int val) {
return val*42;
};
operator () (unsigned int val)
, у вас відсутній набір дужок.
std::sort()
, або std::for_each()
.
auto
для оголошення змінної. Stroustrup наводить приклад: function<void(char*b, char*e)> rev=[](char*b, char*e) { if( 1<e-b ) { swap( *b, *--e); rev(++b,e); } };
для обернення рядка заданих покажчиків початку та кінця.
Немає.
Що ти намагаєшся зробити?
вирішення:
int main(void)
{
struct foo
{
void operator()() { int a = 1; }
};
foo b;
b(); // call the operator()
}
Починаючи з C ++ 11, ви можете використовувати правильні лямбдаши . Інші відповіді див. Для отримання більш детальної інформації.
Стара відповідь: Ви можете, начебто, але вам доведеться обдурити і використовувати манекен клас:
void moo()
{
class dummy
{
public:
static void a() { printf("I'm in a!\n"); }
};
dummy::a();
dummy::a();
}
Як уже згадували інші, ви можете використовувати вкладені функції, використовуючи розширення мови gnu в gcc. Якщо ви (або ваш проект) дотримуєтесь ланцюжка інструментів gcc, ваш код буде здебільшого переносним для різних архітектур, на які спрямований компілятор gcc.
Однак якщо є можлива вимога, що вам може знадобитися компілювати код з іншою ланцюжком інструментів, я б не тримався осторонь таких розширень.
Я б також з обережністю ступав під час використання вкладених функцій. Вони є прекрасним рішенням для управління структурою складних, але згуртованих блоків коду (фрагменти яких не призначені для зовнішнього / загального використання.) Вони також дуже корисні для контролю забруднення простору імен (дуже реальна проблема з природно складними / тривалі заняття з багатослівних мов.)
Але як і все, вони можуть бути відкритими для зловживань.
Сумно, що C / C ++ не підтримує такі функції як стандарт. Більшість варіантів Pascal і Ada (майже всі мови на основі Algol). Те саме з JavaScript. Те саме з сучасними мовами, як Scala. Те саме з поважними мовами, такими як Erlang, Lisp або Python.
І так само, як і для C / C ++, на жаль, Java (з якою я заробляю більшу частину життя).
Тут я згадую Java, бо бачу кілька плакатів, які пропонують використання класів та методів класу як альтернативи вкладеним функціям. І це також типовий спосіб вирішення Java.
Коротка відповідь: Ні.
Це, як правило, вводить штучну, непотрібну складність в ієрархію класів. При рівних обставинах ідеальною є наявність ієрархії класів (та її охоплюючих просторів імен та областей), що представляє фактичний домен якомога простіше.
Вкладені функції допомагають розібратися з "приватною", всередині функціональної складності. Не маючи цих можливостей, слід намагатися уникати поширення цієї "приватної" складності і перетворюватися на свою класову модель.
У програмному забезпеченні (і в будь-якій інженерній дисципліні) моделювання - справа компромісів. Таким чином, у реальному житті будуть виправдані винятки з цих правил (а точніше - вказівок). Однак, будьте обережні.
Ви не можете мати локальні функції в 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's 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's 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's 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;
}
Ні, це не дозволено. Ні C, ні C ++ не підтримують цю функцію за замовчуванням, проте TonyK зазначає (у коментарях), що до компілятора GNU C є розширення, які дозволяють цю поведінку в C.
Всі ці трюки просто виглядають (більш-менш) як локальні функції, але вони не працюють так. У локальній функції ви можете використовувати локальні змінні її суперфункцій. Це свого роду напівглобали. Не з цих хитрощів можна це зробити. Найближчим є лямбда-трюк від c ++ 0x, але його закриття пов'язане у часі визначення, а не в часі використання.
Ви не можете визначити вільну функцію всередині іншої в C ++.
Дозвольте мені розмістити тут рішення для C ++ 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 після компіляції).