次の不適切なコードを考えてみましょう。
#include <string>
#include <iostream>
struct C {
C(const std::string &_s): s(_s) { }
void run() {
std::cout << "Here we are using the string " << s << "\n";
}
const std::string &s;
};
int main() {
C a("/tmp/example.log");
a.run();
return 0;
}
g++ 6.3.0 as clang 3.8.1 は警告なしでこのコードをコンパイルし-W -Wall -Werror
ます。
Here we are using the string Here we are usin
単純に期待できる結果が得られないのはなぜですか? std::string
から作成されたオブジェクトの有効期間は const char *
、コンストラクターのスコープに制限されます。したがって、foo()
コンストラクターが戻ると、foo::s
フィールドには存在しないオブジェクトへの参照があります。カブーム。
誰かがアイデアを理解していれば、修正を導入するのは簡単です。オブジェクトを明示的に作成std::string
し、それをパラメーターとして渡すことができます。
std::string s("/tmp/example.log");
C a(s);
foo::s
もう1つは、フィールド宣言を次のように変更することで、少し安全だと思います。
const std::string s;
パラメータの変更不可能なローカル コピーを作成しますが、オブジェクトをコピーするコストが追加されます。さて、この例では std::string をコピーしても自動的にディープ コピーが作成されるわけではありませんが、独自に実装されたクラスの場合はまったく別の問題になる可能性があります。
C++11 の場合、この回答で指定されているように、利用可能な他の方法はほとんどありませんが、たとえば、組み込み Linux 環境では、古いコンパイラといくつかのレガシー コードにこだわっている場合、必ずしも重要なオプションではありません。
ただし、プログラマーがどのような解決策を選択するにしても、問題があることを知っておく必要があります。const char *
自分のコードを適切に書いたとしても、他の開発者がパラメーターを渡すという罠に陥った場合、コンパイラーはそのような状況を明らかに危険なものとして検出するのに十分賢明であると漠然と思っていました。しかし、コンパイラの動作をチェックするコードを書くことにしましたが、がっかりしました。既に存在しないオブジェクトへの参照を持つという罠に陥りやすいのです。
一時オブジェクトを const 参照として渡すことは危険であると認識されており、C++11 では何らかの形で対処されていますが、そのような動作に積極的に対抗する責任は依然としてプログラマーに委ねられています。それで、コンパイラからの警告を期待するのは合理的ですか、それとも外部の静的分析ツールに頼るべきですか?