Quero dizer algo como:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Quero dizer algo como:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Respostas:
Nas versões atuais do c ++ (C ++ 11, C ++ 14 e C ++ 17), você pode ter funções dentro de funções na forma de um lambda:
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!");
}
}
Lambdas também pode modificar variáveis locais através de ** captura por referência *. Com captura por referência, o lambda tem acesso a todas as variáveis locais declaradas no escopo do lambda. Pode modificá-los e alterá-los normalmente.
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 ++ não suporta isso diretamente.
Dito isto, você pode ter classes locais, e elas podem ter funções (não static
ou static
), para que você possa estender isso até certo ponto, embora seja um pouco complicado:
int main() // it's int, dammit!
{
struct X { // struct's as good as class
static void a()
{
}
};
X::a();
return 0;
}
No entanto, eu questionaria a práxis. Todo mundo sabe (bem, agora que você faz :)
) de qualquer maneira, o C ++ não suporta funções locais, então elas estão acostumadas a não tê-las. Eles não estão acostumados, no entanto, a esse argumento. Eu gastaria um bom tempo nesse código para garantir que ele realmente esteja lá apenas para permitir funções locais. Não é bom.
int main()
eint main(int argc, char* argv[])
int main()
e int main(int argc, char* argv[])
deve ser suportado e outros podem ser suportados, mas todos têm retorno int.
Para todos os efeitos, o C ++ suporta isso via lambdas : 1
int main() {
auto f = []() { return 42; };
std::cout << "f() = " << f() << std::endl;
}
Aqui, f
é um objeto lambda que atua como uma função local no main
. As capturas podem ser especificadas para permitir que a função acesse objetos locais.
Nos bastidores, f
é um objeto de função (ou seja, um objeto de um tipo que fornece um operator()
). O tipo de objeto da função é criado pelo compilador com base no lambda.
1 desde C ++ 11
+1
de mim.
As classes locais já foram mencionadas, mas aqui está uma maneira de deixá-las aparecer ainda mais como funções locais, usando uma sobrecarga de operador () e uma classe anônima:
int main() {
struct {
unsigned int operator() (unsigned int val) const {
return val<=1 ? 1 : val*(*this)(val-1);
}
} fac;
std::cout << fac(5) << '\n';
}
Eu não aconselho a usar isso, é apenas um truque engraçado (pode fazer, mas eu não deveria).
Com a ascensão do C ++ 11 há um tempo, agora você pode ter funções locais cuja sintaxe lembra um pouco o JavaScript:
auto fac = [] (unsigned int val) {
return val*42;
};
operator () (unsigned int val)
faltando um par de parênteses.
std::sort()
, ou std::for_each()
.
auto
para declarar a variável. Stroustrup dá o exemplo: function<void(char*b, char*e)> rev=[](char*b, char*e) { if( 1<e-b ) { swap( *b, *--e); rev(++b,e); } };
para reverter uma string com indicadores de início e fim.
Não.
O que você está tentando fazer?
Gambiarra:
int main(void)
{
struct foo
{
void operator()() { int a = 1; }
};
foo b;
b(); // call the operator()
}
A partir do C ++ 11, você pode usar lambdas apropriadas . Veja as outras respostas para mais detalhes.
Resposta antiga: Você pode, mais ou menos, mas precisa trapacear e usar uma classe falsa:
void moo()
{
class dummy
{
public:
static void a() { printf("I'm in a!\n"); }
};
dummy::a();
dummy::a();
}
Como já mencionado, você pode usar funções aninhadas usando as extensões de idioma gnu no gcc. Se você (ou seu projeto) aderir à cadeia de ferramentas gcc, seu código será mais portátil nas diferentes arquiteturas direcionadas pelo compilador gcc.
No entanto, se houver um requisito possível de que você possa precisar compilar código com uma cadeia de ferramentas diferente, eu ficaria longe dessas extensões.
Eu também andaria com cuidado ao usar funções aninhadas. Elas são uma solução bonita para gerenciar a estrutura de blocos de código complexos e ainda coesos (cujas partes não são para uso externo / geral). Eles também são muito úteis no controle da poluição do espaço para nome (uma preocupação muito real com a complexidade natural / classes longas em idiomas detalhados.)
Mas, como qualquer coisa, eles podem estar abertos a abusos.
É triste que o C / C ++ não suporte recursos como padrão. A maioria das variantes pascal e Ada fazem (quase todas as linguagens baseadas em Algol). Mesmo com JavaScript. Mesmo com línguas modernas como Scala. O mesmo acontece com linguagens veneráveis como Erlang, Lisp ou Python.
E, assim como no C / C ++, infelizmente o Java (com o qual ganho a maior parte da minha vida) não.
Menciono Java aqui porque vejo vários pôsteres sugerindo o uso de classes e métodos de classe como alternativas às funções aninhadas. E essa também é a solução típica em Java.
Resposta curta: Não.
Fazer isso tende a introduzir uma complexidade artificial desnecessária em uma hierarquia de classes. Com todas as coisas iguais, o ideal é ter uma hierarquia de classes (e seus abrangentes namespaces e escopos) representando um domínio real o mais simples possível.
Funções aninhadas ajudam a lidar com a complexidade "particular", dentro da função. Na falta dessas instalações, deve-se evitar a propagação dessa complexidade "privada" para dentro do modelo de classe.
No software (e em qualquer disciplina de engenharia), a modelagem é uma questão de troca. Assim, na vida real, haverá exceções justificadas para essas regras (ou melhor, diretrizes). Continue com cuidado, no entanto.
Você não pode ter funções locais em C ++. No entanto, o C ++ 11 possui lambdas . Lambdas são basicamente variáveis que funcionam como funções.
Um lambda tem o tipo std::function
( na verdade isso não é bem verdade , mas na maioria dos casos você pode supor que sim). Para usar esse tipo, você precisa #include <functional>
. std::function
é um modelo, tendo como argumento o tipo de retorno e os tipos de argumento, com a sintaxe std::function<ReturnType(ArgumentTypes)
. Por exemplo, std::function<int(std::string, float)>
é um lambda retornando um int
e recebendo dois argumentos, um std::string
e um float
. O mais comum é std::function<void()>
que nada retorna e não aceita argumentos.
Depois que um lambda é declarado, ele é chamado como uma função normal, usando a sintaxe lambda(arguments)
.
Para definir um lambda, use a sintaxe [captures](arguments){code}
(existem outras maneiras de fazer isso, mas não vou mencioná-las aqui). arguments
são os argumentos que o lambda usa e code
é o código que deve ser executado quando o lambda é chamado. Geralmente você coloca [=]
ou [&]
como captura. [=]
significa que você captura todas as variáveis no escopo em que o valor é definido por valor, o que significa que elas manterão o valor que tinham quando o lambda foi declarado. [&]
significa que você captura todas as variáveis no escopo por referência, o que significa que elas sempre terão seu valor atual, mas se forem apagadas da memória, o programa falhará. aqui estão alguns exemplos:
#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;
}
Você também pode capturar variáveis específicas especificando seus nomes. Apenas especificar seu nome os capturará por valor, especificando seu nome com um &
antes os capturará por referência. Por exemplo, [=, &foo]
capturará todas as variáveis por valor, exceto as foo
que serão capturadas por referência, e [&, foo]
capturará todas as variáveis por referência, exceto as foo
que serão capturadas por valor. Você também pode capturar apenas variáveis específicas, por exemplo [&foo]
, capturará foo
por referência e não capturará outras variáveis. Você também pode capturar nenhuma variável usando []
. Se você tentar usar uma variável em um lambda que não capturou, ela não será compilada. Aqui está um exemplo:
#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;
}
Você não pode alterar o valor de uma variável que foi capturada por valor dentro de uma lambda (variáveis capturadas por valor têm um const
tipo dentro da lambda). Para fazer isso, você precisa capturar a variável por referência. Aqui está um exemplo:
#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;
}
Além disso, chamar lambdas não inicializadas é um comportamento indefinido e geralmente causa falha no programa. Por exemplo, nunca faça isso:
std::function<void()> lambda;
lambda(); //Undefined behavior because lambda is uninitialized
Exemplos
Aqui está o código para o que você queria fazer na sua pergunta usando lambdas:
#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;
}
Aqui está um exemplo mais avançado de um lambda:
#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;
}
Não, não é permitido. Nem C nem C ++ suportam esse recurso por padrão, no entanto, TonyK aponta (nos comentários) que existem extensões para o compilador GNU C que permitem esse comportamento em C.
Todos esses truques parecem (mais ou menos) funções locais, mas não funcionam assim. Em uma função local, você pode usar variáveis locais de suas super funções. É meio que semi-globais. Muitos desses truques podem fazer isso. O mais próximo é o truque lambda do c ++ 0x, mas seu fechamento é limitado no tempo de definição, não no tempo de uso.
Você não pode definir uma função livre dentro de outra em C ++.
Deixe-me postar uma solução aqui para C ++ 03 que considero a mais limpa possível. *
#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;
}
(*) no mundo C ++, usando macros nunca é considerado limpo.
Mas podemos declarar uma função dentro de main ():
int main()
{
void a();
}
Embora a sintaxe esteja correta, às vezes pode levar à "Análise mais irritante":
#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());
}
=> sem saída do programa.
(Somente clang aviso após a compilação).