Я имею в виду что-то вроде:
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
от меня.
Локальные классы уже были упомянуты, но вот способ, позволяющий им выглядеть еще больше как локальными функциями, используя перегрузку 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 не должен).
С появлением C ++ 11 некоторое время назад вы можете иметь локальные функции, синтаксис которых немного напоминает JavaScript:
auto fac = [] (unsigned int val) {
return val*42;
};
operator () (unsigned int val)
, у вас пропущен набор скобок.
std::sort()
, или std::for_each()
.
auto
для объявления переменной. Страуструп дает пример: 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 ++ не поддерживает такие функции как стандарт. Большинство вариантов Паскаля и Ада делают (почти все языки на основе Алгола). То же самое с 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;
}
(*) в мире C ++ использование макросов никогда не считается чистым.
Но мы можем объявить функцию внутри 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 предупреждение после компиляции).