変数定義構文の明白な機能

完全に無邪気に見えるC ++コードが提案されています。 テンプレートも仮想関数も継承もありませんが、この素晴らしい言語の作成者は、きれいなフィールドの真ん中にすくいを隠しました。



struct A {

A ( int i) {}

};



struct B {

B (A a) {}

};



int main () {

int i = 1;

B b(A(i)); // (1)

return 0;

}




* This source code was highlighted with Source Code Highlighter .








質問:どのタイプの変数b? 一見すると推測できるものではありません。





分析



もちろん、変数bの型はBではありません。そうでなければ、この記事はそうではありませんでした:)すぐには答えませんが、代わりに、1000ページの標準を掘り下げずにどのように到達できるかを説明します。



最初に、デバッグ印刷を追加します。

#include <iostream>

struct A {

A ( int i) { std::cout << 'A' ;}

};



struct B {

B (A a) { std::cout << 'B' ;}

};



int main () {

int i = 1;

B b(A(i)); // (1)

return 0;

}



* This source code was highlighted with Source Code Highlighter .








このコードを実行しようとすると、 も出力されないことがわかります 。 しかし、行(1)を

B b(A(1));







突然すべてが機能し始めます。



次に、可能な限り警告をオンにしてコンパイラーの出力を注意深く見てみましょう。

$ g++ -W -Wall test.cpp

x.cpp:2: warning: unused parameter 'i'

x.cpp:6: warning: unused parameter 'a'

x.cpp: In function 'int main()':

x.cpp:10: warning: unused variable 'i'







最初の2行ですべてが明確になり、実際、コンストラクターのパラメーターは使用されません。 しかし、最後の行は非常に奇妙に見えます。 次の行で使用された場合、どのようにして未使用であることが判明しましたか?



原則として、この情報は質問に答える少しの考えを与えるのに十分です。 しかし、賢明な考えが起こらず、もう少し遊びたい場合は、コンパイラーに聞いてみませんか? RTTIが助けになります。



#include <iostream>

#include <typeinfo>



struct A {

A ( int i) {}

};



struct B {

B (A a) {}

};



int main () {

int i = 1;

B b(A(i)); // (1)

std::cout << typeid(b).name() << std::endl;

return 0;

}



* This source code was highlighted with Source Code Highlighter .








GCC 4.3をコンパイルすると、このプログラムの結果は次のようになります

F1B1AE







変数の型について必要な情報を暗号化します(もちろん、別のコンパイラーが別の行を提供します。出力形式type_info :: name()は標準に記述されておらず、開発者の裁量に任されています)。 C ++ filtは、これらの文字と数字の意味を調べるのに役立ちます。

$ c++filt -t F1B1AE

B ()(A)







答えは次のとおりです。これは、タイプAパラメーターの入力を受け取り、タイプBの値を返す関数です。



理由



私たちの行がそのような予期せぬ方法で解釈された理由を理解することは残っています。 問題は、変数の型宣言では、名前を囲む余分な角かっこは無視されることです。 たとえば、次のように書くことができます

int (v);







そしてそれはまったく同じことを意味します

int v;









したがって、長い苦労の行(1)は、意味を変更せずに書き直し、余分な括弧のペアを削除できます。

B b(A i);







これで、bがタイプBの値を返すタイプAの1つの引数を持つ関数宣言であることを肉眼で確認できます。



同時に、未使用の変数iについての奇妙な変動について説明しました。実際、仮パラメータiとは関係ありません。



回避策



コンパイラーに本当に必要なものをコンパイラーに説明する必要があります。つまり、タイプAの変数で初期化されたタイプBの変数を取得します。最も簡単な方法は、次のようにブラケットを追加することです。

B b((A(i)));







または:

B b((A)(i));







これは、これが関数宣言ではないことをパーサーに確信させるのに十分です。



または、コンストラクターが明示的に宣言されていない限り、割り当てを使用してコンストラクター呼び出しフォームを使用できます。

B b = A(i);







「=」記号が存在するにもかかわらず、ここでは不要なコピーは発生しません。これは、クラスBにプライベートコピーコンストラクターを用意することで簡単に確認できます。



または、追加の変数を入力するだけです:

A a(i);

B b(a);







確かに、これには変数aの不必要なコピーが必要になりますが、多くの場合これは受け入れられます。



より理解しやすいと思われる方法を選択してください:)



StackOverflowの投稿に触発された



PS説明をありがとう。




All Articles