2017-03-06 4 views
4
// g++ --std=c++17 test.cpp -I /usr/local/include -L /usr/local/lib -lboost_system -Wall -pedantic -Wreturn-type -Wstrict-aliasing -Wreturn-local-addr -fsanitize=address -g 
// LD_LIBRARY_PATH=/usr/local/lib ./a.out 

#include <iostream> 
#include <boost/filesystem.hpp> 

namespace fs = boost::filesystem; 

class A { 
public: 
    fs::path path_; 

    const fs::path & path() const { return path_; } 
    fs::path & path() { return path_; } 
}; 

class B { 
public: 
    fs::path root_path_; 

    A path_2; 
    A path_3; 

    const fs::path & operator()() const { 
     for (const auto & path : { 
      path_3.path(), 
      path_2.path(), 
      root_path_ 
     }) { 
      if (not path.empty()) { 
       return path; 
      } 
     } 
     throw std::logic_error{"using default-constructed B"}; 
    } 
}; 

int main(int argc, char **argv) { 
    B b; 
    b.root_path_ = "foo/"; 
    b.path_2.path() = "foo/bar"; 
    b.path_3.path() = "foo/baz"; 

    std::cout << b() << '\n'; 

    return 0; 
} 

上記のコードは、私が知っている限り、有効なC++と思われます。代わりに、呼び出されるとガベージ出力が表示されます。変数のループのためのrangedは、ローカル変数のアドレス参照を返しますか?

g++は、最初に文句を言いませんが、アドレスサニタイザーになります。 -O2を追加すると、最終的にg++が文句を言う。私が代わりにポインタを使用してエラーを解決

$ cat /etc/fedora-release 
Fedora release 25 (Twenty Five) 
$ g++ --version 
g++ (GCC) 6.3.1 20161221 (Red Hat 6.3.1-1) 
Copyright (C) 2016 Free Software Foundation, Inc. 
This is free software; see the source for copying conditions. There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 

注:生成された警告は、私が使用しています

test.cpp: In member function ‘const boost::filesystem::path& B::operator()() const’: 
test.cpp:31:12: warning: function may return address of local variable [-Wreturn-local-addr] 
    return path; 
      ^~~~ 
test.cpp:29:3: note: declared here 
    }) { 
^

注意です。

const fs::path & operator()() const { 
      for (const auto * path : { 
        &path_3.path(), 
        &path_2.path(), 
        &root_path_ 
      }) { 
        if (not path->empty()) { 
          return *path; 
        } 
      } 
      throw std::logic_error{"using default-constructed B"}; 
    } 

しかし、私の心の中でいくつかの質問を残す行います-O2が追加されるまで

  1. なぜg++ない問題について文句を言うのでしょうか?
  2. 私のコードは正確には何のために定義されていませんか?私はそれがよく定義されていると言うでしょう:B::operator() constは...まあ... const。つまり、のオブジェクトがローカルまたはconstメンバーのいずれかであることを意味します。 constメンバーにアクセスします。それはローカル変数const auto &を構築します。... constメンバーフィールドを参照する必要があります。まさにそれが一時的にバインドされる原因は何ですか?
+1

参照が最初に返されるのはなぜですか? – chris

+0

この例はスリム化されています。セレクタ関数として 'operator()'を使って、他の関数への入力としてどのメンバ変数を使うかを決めています。なぜ私はそれを変更する必要がない非const(または非const参照)を返しますか? – inetknght

+1

'const'値で返すのは一般的な欠点です。まず、呼び出し側の戻り値は分離されているため、関数が変更を行うかどうかは関係ありません。第二に、RVOを阻害する。とにかく、あなたが実際のコードでイニシャライザーリストを使用している場合、言語が最終的に移動できるように変更されない限り、コピーが必要なので、それはうまく終了しません。 – chris

答えて

5
  1. コンパイラは、未定義の動作のための診断を発行する義務を負いません。コンパイラが構文上有効なコードを検出できても、定義されていない動作が発生した場合、それについて不平を言うと、それはケーキの上に氷結するだけです。 gccの-O2は追加の最適化を有効にし、追加のコード解析を行います。そのようなgccは、最適化が有効になっている場合にのみ、未定義の動作を検出することがあります。

  2. あなたの範囲の反復がstd::initializer_list一時的の上にあることが表示されます。範囲繰り返し変数は、初期化子リストへの参照です。そのため、関数は一時的な参照を返すことになります。これは、ここでgccが吠えているものです。メソッドが返ってきたら一時的に破棄されるので、メソッドは破棄されたオブジェクトへの参照を返します。その参照の使用には、未定義の動作が含まれます。あなたはポインタのリストに一時的な範囲を変換すると

、あなたは値によって反復され、あなたは一時的に参照を返すされるのではなく、完全にコーシャある範囲の値を、derefencingポインタ。以下の宣伝文from here:

The underlying array is not guaranteed to exist after the lifetime of the original initializer list object has ended. The storage for std::initializer_list is unspecified (i.e. it could be automatic, temporary, or static read-only memory, depending on the situation).

範囲の繰り返しにバインドされた初期化子リストは、反復の終了時に終了した場合寿命に

ご注意ください。メソッドからの復帰を含む。そのため、イニシャライザのリストは存在しなくなり、ダングリングリファレンスが返されました。

+3

おそらく、 'initializer_list'がオブジェクトを値で保存しているのではなく、参照ではなくボーナスそれがなぜであるかについての宣伝のポイント)。 –

+0

ええ、標準から引用されたものがあれば、それはおそらく私の答えと記されているでしょう。私は確かにもっと具体的なものがほしいと思うでしょう一時的なイニシャライザリストですが、なぜ一時的に既存のオブジェクトへの参照が含まれていないのですか?明示的な型 'std :: initializer_list {...}'で初期化子リストを前置することで試してみると、エラーになります: '/usr/include/c++/6.3.1/ initializer_list:54:26:エラー:リファレンスタイプのポインタ 'const boost :: filesystem :: path&' 'を作成します。これは、const参照のポインタに対するイテレータを形成するためです。それは何ですか? – inetknght

+0

私が引用した同じリンクでは、イニシャライザリストの内容は常に「コピー初期化」されているため、参照できないようになっています。関連するビットを標準から掘り起こすことは可能ですが、それはいつも雑用であり、cppreference.comは一般的にここで「十分に良い」と考えられています。 –

関連する問題