明確にするために、返品タイプの役に立たないタイプの予選を使用する必要がありますか?

StackOverflow https://stackoverflow.com/questions/1579435

質問

私たちの静的分析ツールは、次のようなヘッダーファイルにプロトタイプがある場合、「返品タイプの役に立たないタイプ予選」について不満を言います。

const int foo();

このように定義しました。これは、APIがより明確であると考えて、変化しない定数を返しているためです。 const 所定の位置に。

これは、すべてのグローバルが明示的に初期化されない場合、すべてのグローバルがゼロに初期化されるとすでに述べているにもかかわらず、明確にするためにグローバル変数を明示的にゼロに明示的に初期化することに似ていると思います。一日の終わりには、それは本当に問題ではありません。 (しかし、静的分析ツールはそれについて文句を言いません。)

私の質問は、これが問題を引き起こす可能性がある理由はありますか?ツールによって生成されたエラーを無視する必要がありますか、それとも、明確で一貫したAPIの可能性のあるコストでツールを配置する必要がありますか? (他のものを返します const char* ツールに問題がないという定数。)

役に立ちましたか?

解決

通常、コードが何が起こっているのかをできるだけ正確に記述する方が良いです。あなたはこの警告を受けています constconst int foo(); 基本的に意味がありません。 APIは、何がわからない場合にのみ明確に見えます const キーワードは意味があります。そのような意味をオーバーロードしないでください。 static それは十分に悪いことであり、より多くの混乱の可能性を追加する理由はありません。

const char * より違うものを意味します const int そうです、だからあなたのツールはそれについて文句を言いません。前者は一定の文字列へのポインターです。つまり、そのタイプを返す関数を呼び出すコードは、文字列の内容を変更しようとしないようにします(たとえば、ROMにある可能性があります)。後者の場合、システムは返品に変更を加えないことを強制する方法がありません int, 、したがって、予選は無意味です。リターンタイプに類似しているのは次のとおりです。

const int foo();
char * const foo2();

どちらも静的分析に警告を与えます - const修飾子を返品値に追加することは意味のない操作です。 AA参照パラメーター(または戻りタイプ)がある場合にのみ理にかなっています。 const char * 例。

実際、私はちょっとしたテストプログラムを作成しましたが、GCCはこの問題について明示的に警告しています。

test.c:6: warning: type qualifiers ignored on function return type

したがって、不平を言っているのはあなたの静的分析プログラムだけではありません。

他のヒント

別の手法を使用して、ツールを不幸にすることなく、意図を説明できます。

#define CONST_RETURN

CONST_RETURN int foo();

問題はありません const char * それは、一定のポインターではなく、一定のcharへのポインターを宣言しているからです。

無視します const 今のところ、 foo() 値を返します。できるよ

int x = foo();

返された値を割り当てます foo() 変数に x, 、あなたができるのとほぼ同じ方法で

int x = 42;

値を割り当てます 42 変数xに。
ただし、変更することはできません 42 ...または返された値 foo(). 。値はから返されたと言っています foo() 適用して、変更することはできません const タイプのキーワード foo() 何も達成しません。

そうすることはできません const (また restrict, 、 また volatile)。オブジェクトのみがタイプの修飾子を持つことができます。


の対照

const char *foo();

この場合、 foo() ポインターをオブジェクトに返します。返された値によって指されたオブジェクトは資格を得ることができます const.

INTはによって返されます コピー. 。それはconstのコピーかもしれませんが、それが何か他のものに割り当てられている場合、それが割り当て可能であるという事実により、定義上、constであることはできません。

キーワードconstには言語内に特定のセマンティクスがありますが、ここでは本質的にコメントとして誤用しています。明確さを加えるのではなく、言語セマンティクスの誤解を示唆しています。

const int foo() とは大きく異なります const char* foo(). const char* foo() コンテンツが変更されない配列(通常は文字列)を返します。間の違いについて考えてください:

 const char* a = "Hello World";

const int b = 1;

a まだ変数であり、変更できない他の文字列に割り当てることができますが b 変数ではありません。そう

const char* foo();
const char* a = "Hello World\n";
a = foo();

許可されていますが

const int bar();
const int b = 0;
b = bar();

であっても、許可されていません const の宣言 bar().

はい。コードを「明示的に」書くことをお勧めします。なぜなら、コードを読んだときに誰にとっても(あなた自身を含む)ことを明確にするからです。あなたはのためのコードを書いています 読む他のプログラマー, 、コンパイラと静的分析ツールの気まぐれを喜ばないようにしてください!

(ただし、そのような「不要なコード」が別のコードを生成しないことに注意する必要があります!)

読みやすさ/保守性を改善する明示的なコーディングの例:

  • 算術式の一部の周りにブラケットを配置して、自分が起こりたいことを明示的に指定します。これにより、読者に私が意味することを明らかにし、優先順位ルールについて心配する(または間違いを犯す)ことを節約します。

    int a = b + c * d / e + f;      // Hard to read- need to know precedence
    int a = b + ((c * d) / e) + f;  // Easy to read- clear explicit calculations
    

  • C ++では、仮想関数をオーバーライドする場合、派生クラスでは「仮想」に言及せずに宣言できます。コードを読んでいる人なら誰でも、それが仮想関数であるとは言えません。ただし、仮想キーワードを安全に使用できます。

    virtual int MyFunc()
    そして、これにより、この方法が仮想であることがクラスのヘッダーを読んでいる人には明らかになります。 (この「C ++構文バグ」は、この場合に「オーバーライド」キーワードを使用することを要求することによりC#で修正されます。「不要な仮想」を逃すことが本当に悪いアイデアです。

これらはどちらも明確な例です。「不要な」コードを追加すると、コードがより読みやすくなり、バグが発生しやすくなります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top