基本的な C++ 宣言構文は、単純な左から右への構文ではありません<type> <identifier>
。
これが C++ の構文である場合、たとえば、int の配列を宣言すると、型のint[]
後に識別子が続きます。
int[] foo
関数を宣言するということは、次のような関数型を記述し、int()
その後に識別子を付けることを意味します。
int() foo;
代わりに、C から継承された C++ の構文は、「宣言は使用を模倣する」と呼ばれるスタイルです。
<type_identifier> <expression>;
宣言内の式は、宣言されたエンティティを使用する方法のように見え、その使用法によって指定された型が生成されます。
例:
int i; // now the expression i has the type int
int (ii); // now the expression (ii) has the type int, and so ii has the same type
int j(); // now the expression j() has type int, and therefore j has a function type
int k[3]; // now the expression k[3] has type int, and therefore k has an array type
int (*l)(); // the expression (*l)() has type int, and therefore (*l) has a function type, therefore l has a pointer-to-function type.
などなど、配列のインデックス演算子、関数呼び出し演算子、括弧、逆参照演算子、ビットごとの AND 演算子、そしておそらく私が覚えていないいくつかの演算子を含む任意の複雑さがあります。さらに、さまざまなポイントで const と volatile を投入できます。
さらに悪いことに、宣言構文は初期化子の構文と奇妙な相互作用をする可能性があります。たとえば、「最も厄介な解析」は、初期化子の括弧が関数型の宣言に関係する括弧と混同されている状況です。括弧が型式の一部なのかイニシャライザの一部なのかは、内部で使用されている名前によって異なります。
struct bar {};
int baz() { return 1; }
int foo(bar()); // declaration of function (most vexing parse)
int foo2(baz()); // declaration of variable (initialized with baz())
もちろん、他の人が言及した問題もあります。typedef/type エイリアス、マクロ、ネストされた宣言、複数の宣言など。