constの正確さで私を売る


133

では、なぜconstをできるだけ頻繁に使用することが常に推奨されているのでしょうか。constを使用することは、C ++のヘルプよりも面倒なことのようです。しかし、繰り返しますが、私はこれをpythonの観点から考えています。何かを変更したくない場合は、変更しないでください。つまり、ここでいくつかの質問があります:

  1. constとしてマークするたびにエラーが発生し、constになるように他の関数をどこかに変更する必要があるようです。次に、これにより、別の機能をどこかで変更する必要があります。これは経験を積むだけで簡単になるものですか?

  2. constを使用するメリットは、問題を補うのに本当に十分ですか?オブジェクトを変更するつもりがない場合は、それを変更しないコードを作成しないでください。

この時点では、正確性と保守性の目的でconstを使用することの利点に最も焦点を合わせていることに注意してください。ただし、パフォーマンスへの影響を理解するのも良いことです。


1
8歳の振り返りですが、コードを100倍に高速化してみてはいかがですか(このライブで確認)。
lorro

1
ここで私の回答を確認してください(関連する質問、私は同じ回答だと思います):stackoverflow.com/questions/42310477/…ところで、私は大好きですconst
Gines Hidalgo

回答:


158

これは、「constの正確さ」に関する決定的な記事です:https : //isocpp.org/wiki/faq/const-correctness

簡単に言えば、constを使用するのは良い習慣です...

  1. 意図的に変更されない変数を誤って変更することから保護します。
  2. 誤って変数を割り当てないように保護します。
  3. コンパイラはそれを最適化できます。たとえば、あなたはから保護されています

    if( x = y ) // whoops, meant if( x == y )

同時に、コンパイラーは変数/関数の状態を常に正確に把握しているため、より効率的なコードを生成できます。タイトなC ++コードを記述している場合は、これで十分です。

const-correctnessを一貫して使用するのは難しい場合がありますが、エンドコードはより簡潔で安全にプログラムできます。多くのC ++開発を行う場合、これの利点はすぐに現れます。


私は常にconst値が自由にキャッシュされ、それによって多くの同時実行の問題を回避したり、速度を改善したりできると思っていました。本当?
フィルH

3
承知しました。 const変数、コンパイラーが変数の完全な意図と使用法を知っているため、より最適なコードを生成できるという意味で、速度向上させることができます。違いに気づきますか?まあ、それはあなたのアプリケーションで議論の余地があります。並行性に関する限り、const変数は読み取り専用です。つまり、値は常に同じであるため、これらの変数に対する排他ロックは必要ありません。
ジョーダンパーマー2013

4
C ++ 11以降、標準ライブラリconstはスレッドセーフを意味し、独自のコードを処理することを想定しているため、マルチスレッドの問題のチェックが容易になり、APIユーザーにAPI保証を提供する簡単な方法になります。
pmr 2014年

3
私にとって、constの正確さの最大の付加価値の1つは、ポインターが関数によって変更されないデータ(つまり、入力のみ)を参照しているときに、関数のプロトタイプを見るだけでわかることです。
cp.engr 2017年

1
論理的および物理的な定数があることを忘れないでください。変更可能なものとしてマークされている、含まれている集約オブジェクトは変更できますが、そのようにマークすることは、含まれているオブジェクトの論理的な一貫性とは何の関係もないことを意味します。
エイドリアン

128

以下に、constの正確性から保護できる一般的なエラーのあるコードを示します。

void foo(const int DEFCON)
{
   if (DEFCON = 1)     //< FLAGGED AS COMPILER ERROR! WORLD SAVED!
   {
       fire_missiles();
   }
}

19
つまり、Cの代入演算子として流血な馬鹿が "="を選択したため、constが必要だということです。;-)
スティーブジェソップ

37
もちろん、最新のコンパイラのほとんどは条件文の代入について警告し、警告をエラーフラグとして扱います。右:->
Jack Bolding

7
その例をあまり難しくしないでください。これは、if(i == 0)ではなくif(0 == i)を使用するように説得するために一部の開発者によって伝道された同じ例です。ついに、Dougのコードパターンを "if"ステートメントの外で使用できるようになりました。重要なのは、それがユーモラスな方法でconstの利点の1つを示していることです。+ 1.
paercebal 2008年

2
一部のコンパイラ(およびlints)はこれについて長い間警告してきましたが、これはこの誤植を捉えるのにconst:codepad.org/Nnf5JUXVよりもはるかに有用です。

7
@ロジャーあなたは私たちがビルドがクリーンで警告のない世界に住んでいると仮定します。私の経験では、多くのコードで警告がノイズで失われます。また、if式で代入を行うコードはたくさんあり、多くの人はそれが有効な「良い」スタイルであると主張しています。
Doug T.

62

constとしてマークするたびにエラーが発生し、constになるように他の関数をどこかで変更する必要があるようです。次に、これにより、別の機能をどこかで変更する必要があります。これは経験を積むだけで簡単になるものですか?

経験から、これは完全な神話です。確かに、const-correctがconst-correctコードと一緒に配置されている場合に発生します。最初からconst-correctを設計する場合、これが問題になることはありません。何かconstを作成しても、それ以外は準拠しない場合、コンパイラーは非常に重要な何かを通知しているので、時間をかけて適切に修正する必要があります


7
これは、const関数が非const関数を呼び出そうとしていたため、その下のデータを変更するのを忘れていたために、多くのバグを防ぐのに役立ちました。
Mooing Duck 2013

9
常にクリーンなコードベースから始める人はほとんどいません。ほとんどのコーディングは、引用されたコメントが非常に正確なレガシーコードのメンテナンスです。新しいリーフ関数を作成するときはconstを使用しますが、不慣れなコードを6ダースまたは12ダースの呼び出しレベルで追跡する価値があるかどうかを質問します。
ウォーレンデュー

3
これは私の経験では完全に間違っています。ネストされたポイントタイプは、この種の問題の最大の悩みの種です。1つのタイプに複数のconst数量詞を含めることができます。
Noldorin

4
「最初からconst-correctを設計する場合」実際にconst-correctnessを最初から強制する贅沢を何回持っていますか?私たちのほとんどは、これが当てはまらず、あなたがそれらに対して行うことができることがほとんどない毎日のベースで多くのAPIとライブラリを扱わなければなりません。OPの感情に同意します。「constとしてマークするたびにエラーaが発生するようです...」
nyholku

@WarrenDewこれは推移的な議論です。元の作者がconst適切に(つまり、「最初から」)使用していた場合、実際には問題はないはずです。それがまさにポイントです!
オービットのライトネスレース

31

最初にコードを書いているとき、それはあなたのためではありません。それは、クラスまたはインターフェース内のメソッド宣言を見て、それが何をするのかを見ている誰か(または数か月後のあなた)のためのものです。オブジェクトを変更しないことは、そこから収集する重要な情報です。


1
これは一種の真実です。Constは、インターフェイスなどを通じて変数を強制するための保護としても使用されます。
ジョーダンパーマー

それはそうですが、規律あるコーディングの実践とポスターの状態を通じてそれを強制することができます。本当のメリットは、これがAPIに反映されていることです。
アントニオヘイリー

2
丁度。"const int Value = 5;"にすることをお勧めします。「int ConstValue = 5;」よりも。+1。
paercebal 2008年

6
const-correctnessを入れる適切なタイミングは、最初にAPIを作成するときです。そうしないと、改造したときにconst-poisoningで問題が発生します(これが、古いコードに追加することと、新しいコードで実行することとはまったく異なる理由です)。
ドナルフェロー

@DonalFellowsこれは後でconstに入れるとは言っていません。すでに存在するconstを使用してコードを読むと、後でメリット得られると言っています
Caleth

27

constを厳密に使用すると、ほとんどの関数に実際の変数がほとんどないことに驚かれるでしょう。多くの場合、ループカウンターにすぎません。コードがそのポイントに達している場合は、温かい感じがします...コンパイルによる検証...関数型プログラミングの領域が近くにあります...すぐに触れることができます...


1
C ++ 17とconstexprの良さから、私はコンパイル時間の単体テストを書いています...さらに近づいています...
QBziZ

22

constは、あなたが開発者として作成している約束であり、コンパイラーの助けを借りて実施します。

const-correctである私の理由:

  • 変数やオブジェクトを変更しないことを関数のクライアントに伝えます
  • const参照で引数を受け入れると、値渡しで安全に参照渡しする効率が得られます。
  • インターフェースをconst correctとして作成すると、クライアントがそれらを使用できるようになります。非const参照を取り込むようにインターフェースを作成する場合、constを使用しているクライアントは、constnessをキャストして、操作する必要があります。インターフェイスが非const char *を受け入れ、クライアントがstd :: stringsを使用している場合、これはconst char *しか取得できないため、これは特に厄介です。
  • constを使用すると、変更を加えてはならないものを誤って変更しないように、コンパイラーが正直になります。

20

私の哲学は、コンパイル時間チェック付きの厳密な言語を使用する場合は、それを最大限に活用することです。 constあなたはどのようなコミュニケーションの仕方施行コンパイラである意味を ...それは、より良いコメントやdoxygenには、これまでになります以上です。あなたは代金を払っていますが、価値を導き出してみませんか?


20

constなしでC ++をプログラミングすることは、安全ベルトをつけずに運転するようなものです。

車に乗るたびに安全ベルトを付けるのは面倒で、365日のうち364日でも無事に到着します。

唯一の違いは、車で問題が発生したときにすぐに感じることですが、constなしのプログラミングでは、クラッシュの原因となった2週間を検索して、誤って関数の引数をめちゃくちゃにしてしまったことがわかるだけです。効率を上げるために非const参照を渡しました。


18

組み込みプログラミングのconst場合、グローバルデータ構造を宣言するときに慎重に使用すると、起動時にRAMにコピーせずに定数データをROMまたはフラッシュに配置することにより、多くのRAMを節約できます。

日常のプログラミングでは、const注意深く使用することで、文字列リテラルやその他の定数グローバルデータを変更しようとするためにクラッシュしたり、予期しない動作をするプログラムを作成することを回避できます。

大規模なプロジェクトで他のプログラマーと一緒に作業する場合、const適切に使用すると、他のプログラマーがあなたをスロットルするのを防ぐのに役立ちます。


13

const背後で「変更」するコードを分離するのに役立ちます。したがって、クラスでは、オブジェクトの状態を変更しないすべてのメソッドをとしてマークしますconst。つまりconst、そのクラスのインスタンスはconstメソッド以外を呼び出すことができなくなります。これにより、オブジェクトを変更する可能性のある機能を誤って呼び出すことが防止されます。

また、constはオーバーロードメカニズムの一部であるため、同じシグネチャを持つ2つのメソッドを持つことができますが、1つはconstあり、もう1つはありません。付きのものconstconst参照用に呼び出され、もう1つは非const参照用に呼び出されます。

例:

#include <iostream>

class HelloWorld {
    bool hw_called;

public:
    HelloWorld() : hw_called(false) {}

    void hw() const {
        std::cout << "Hello, world! (const)\n";
        // hw_called = true;  <-- not allowed
    }

    void hw() {
        std::cout << "Hello, world! (non-const)\n";
        hw_called = true;
    }
};

int
main()
{
    HelloWorld hw;
    HelloWorld* phw1(&hw);
    HelloWorld const* phw2(&hw);

    hw.hw();    // calls non-const version
    phw1->hw(); // calls non-const version
    phw2->hw(); // calls const version
    return 0;
}

13

constの正確さは、最初から実際に配置する必要があるものの1つです。お気づきのように、後で追加するのは大変です。特に、追加する新しい関数と既存の非const-correct関数の間に依存関係がたくさんある場合はなおさらです。

私が作成する多くのコードでは、コンポジションを頻繁に使用する傾向があるため、努力する価値がありました。

class A { ... }
class B { A m_a; const A& getA() const { return m_a; } };

const-correctnessがなければ、複雑なオブジェクトを値で返すことに頼って、クラスBの内部状態を誰も操作していないことを確認する必要があります。

つまり、const-correctnessは、将来の苦痛から身を守るための防御的なプログラミングメカニズムです。


7

Pythonに変数があるとします。あなたはそれを修正することになっていないことを知っています。誤って行った場合はどうなりますか?

C ++は、そもそも本来は実行できないはずの何かを誤って実行することから身を守る方法を提供します。技術的にはとにかくそれを回避することができますが、あなた自身を撃つために追加の仕事をしなければなりません。



3

「const」キーワードを使用すると、クラスに別のインターフェースを指定することになります。すべてのメソッドを含むインターフェースと、constメソッドのみを含むインターフェースがあります。明らかにこれにより、変更したくないものへのアクセスを制限できます。

はい、時間とともに簡単になります。


2

私はconstの正しさが好きです...理論的には。実際に厳密に適用しようとするたびに、最終的には機能しなくなり、const_castはコードを醜くすることで忍び寄り始めます。

多分それは私が使うデザインパターンだけかもしれませんが、constは常に広すぎるブラシになってしまいます。

たとえば、単純なデータベースエンジンを想像してみてください。スキーマオブジェクト、テーブル、フィールドなどがあります。ユーザーが「const Table」ポインタを持っていると、テーブルスキーマ自体を変更することはできません...テーブルに関連付けられたデータ?Insert()メソッドがconstとマークされている場合、データベースを実際に操作するには、内部でconst-nessをキャストする必要があります。constとマークされていない場合は、AddFieldメソッドの呼び出しから保護されません。

たぶん答えは、一定の要件に基づいてクラスを分割することですが、それは、それがもたらす利益のために私が望むよりもデザインを複雑にする傾向があります。


あなたの例はconstを使いすぎた場合だと思いますが、インスタンス変数に適用してconst-nessを削除できるmutable修飾子を忘れないでください。
Zooba

2
Insert()関数は、誤った名前が付けられていない限り、いつconstとマークされますか?モノを追加すると、通常、追加したものが変更されます。つまり、constではありません。あなたが本当に欲しかったのはTableWithConstSchemaでした。
グレッグロジャース

別のクラスを追加することもできますが、const-正確さのためにAPIを純粋に不必要に複雑にしたくありません。
Rob Walker、

1

次のコードのように、コンパイラのヒントにconstも指定できます。

#include <string>

void f(const std::string& s)
{

}
void x( std::string& x)
{
}
void main()
{
    f("blah");
    x("blah");   // won't compile...
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.