戻り値最適化とnoncopyable class

2503 ワード

転載元:http://www.708luo.com/?p=22
今日は出勤します.面白いことがあります.ある同僚にコードを見せてくださいと言われました.このコードはgccでコンパイルできますが、bullseye coverでコンパイルしてカバー率を統計する時に、リンクの段階が間違っていました.
このコードを簡単にします.
class Integer 

{

public:

    Integer(int x=0):_x(x){}

    Integer operator+(const Integer& rhs)

    {

        Integer tmp;

        tmp._x += rhs._x; 

        return tmp;

    }

    ~Integer(){}

private:

    Integer(const Integer&);

    int _x; 

};
私はbullseye coverリンク段階のエラーを見ましたが、つまり「Integer」関数の定義が見つかりません.この関数は「Integer operator+(const Integer&rhs)」で呼び出されました.
 
私が見てみると、bullseye coverの行為は正しいです.operator+の関数は対象です.引用ではなく、コピー構造の操作があるはずです.
なぜgccはリンクして通ることができますか?第一反応は戻り値の最適化で、今回のコピー構造の操作を最適化しました.
コードを書いてテストしてみましたが、やはり値の最適化の原因に戻ります.しかも最適化オプション-O 0でリターン値を開いて最適化しています.これはちょっと意外です.
 
もともと私は、NonCopyable classのコピー構造関数はprvateと宣言すればいいと思っていますが、定義が定義されなくてもいいです.とにかく依存するのはアクセス権限のコントロールです.
今回の経験から、NonCopyable classのコピー構造関数を空と定義することは危険なことだと気づきました.メンバー関数によっても呼び出される可能性がありますので、この時にアクセス権限はもう無力です.関数が定義されていないので、提示するしかないです.
 
間違いに関しては、二つのコンパイラの行為は受け入れられると思います.修正するのはこのコードの編纂方式です.Noncopy classであり、メンバー関数はまた対象事例に戻ります.
しかし、gccは確かに優れています.最適化だけではなく、いくつかの関数の呼び出しを削除したため、必要ではない関数のコードを変更してコンパイルしました.
 
もともと問題はここで終わりました.gccにはもっと面白い現象があると思います.
NonCopyable classを呼び出して、コピー構造が非メンバ関数である場合、コンパイル段階はアクセス権限のエラーを提示します.しかし、コンパイルを通じて最適化することができますが、この関数は呼び出しされません.アクセス制御をバイパスすれば、例えば、その非メンバー関数をNonCopyable classの友元関数として宣言するなど、完全にコンパイルできます.
次のコード:
class Integer 

{

public:

    friend Integer add(const Integer&,const Integer&);

    Integer(int x=0):_x(x){}

    ~Integer(){}

    int get()const{return _x;}

    void set(int x){_x=x;}

    Integer& operator=(const Integer& rhs)

    {

        this->_x = rhs._x; 

        return *this;

    }

private:

    Integer(const Integer&);

    int _x; 

};



Integer add(const Integer& lhs,const Integer& rhs)

{

    Integer tmp;

    tmp.set(lhs.get()+rhs.get());

    return tmp;

}
上のコードは、4行目のコメントを外すと、コンパイル段階では「Integer:Integer」「is prvate」というエラーがあります.反対のコメントの4行目はコンパイル、リンクが通ります.
実は上のコードはまだ全部書いていません.もしアドド関数を呼んだところがあれば、そこで「Integer:Integer(const Integer&)」is prvateというエラーを報告します.呼び出し先のコードも権限制御を迂回する必要があります.
コードの最適化をコードアクセス権限を確認する前に操作すれば、この問題を回避できると思います.しかし、コンパイラの下の実装はよく分かりません.この調整が可能かどうかは分かりません.
あるいは、私たちはNonCopyable classを書く時、私有のコピー構造関数ではなく、公有のコピー構造関数を宣言しますが、それを定義しません.
このように何か弊害があるか分かりません.間違い以外にヒントが遅くなります.しかし、このようなメリットは、前述の「冤罪」の場合は避けられます.