私は次のようなものを意味します:
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()
)があります。関数オブジェクトタイプは、ラムダに基づいてコンパイラによって作成されます。
C ++ 11以降1
+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
変数の宣言には使用できません。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();
}
他の人が述べたように、gccのgnu言語拡張を使用することにより、ネストされた関数を使用できます。あなた(またはあなたのプロジェクト)が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
と二つの引数、1服用std::string
して1を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;
}
名前を指定して、特定の変数をキャプチャすることもできます。それらの名前を指定するだけで値によってそれらをキャプチャし、&
beforeでそれらの名前を指定すると参照によってそれらをキャプチャします。たとえば、[=, &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は(コメントの中で)Cでこの動作を可能にするGNU Cコンパイラの拡張機能があることを指摘しています。
このすべてのトリックは、ローカル関数のように見えますが(多かれ少なかれ)、そのようには機能しません。ローカル関数では、そのスーパー関数のローカル変数を使用できます。セミグローバルのようなものです。これらのトリックのどれもそれを行うことができません。最も近いのはc ++ 0xのラムダトリックですが、そのクロージャは使用時間ではなく定義時間にバインドされています。
可能な限りクリーンだと考える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警告のみ)。