6

このコードがあります:

#include <iostream>

class F {
public:
   F() = default;
   F(F&&) {
      std::cout << "F(F&&)" << std::endl;
   }
   F(F&) {
      std::cout << "F(F&)" << std::endl;
   }
};

class G {
   F f_;
public:
   G(F&& f) : f_(f) {
      std::cout << "G()" << std::endl;
   }
};

int main(){
   G g = F();
   return 0;
}

出力は次のとおりです。

F(F&)
G()

クラスのコンストラクターでコンストラクターのF(F&)代わりにコンストラクターが呼び出されるのはなぜですか?クラスのコンストラクターのパラメーターは右辺値参照ですが、左辺値参照のコンストラクターが呼び出されます。F(F&&)GGF&& f

4

1 に答える 1

11

クラスGのコンストラクターでF(F &&)コンストラクターの代わりにF(F&)コンストラクターが呼び出されるのはなぜですか?

fは左辺値だからです。右辺値にバインドされており、その型はへの右辺値参照ですが、名前付き変数Fでもあります。それはそれを左辺値にします。オブジェクトの値のカテゴリはそのタイプによって決定されないことを忘れないでください。その逆も同様です。

左辺値を関数に渡す場合、左辺値参照のみを関数にバインドできます。右辺値のみをキャッチする場合は、コードを次のように変更する必要があります。

class G {
    F f_;
public:
    G(F&& f) : f_(std::move(f)) {
       std::cout << "G()" << std::endl;
    }
};

または、を使用することもできます。これはこの場合は同等ですが、転送std::forward<>()の意図がさらに明確になります。 f

class G {
    F f_;
public:
    G(F&& f) : f_(std::forward<F>(f)) {
       std::cout << "G()" << std::endl;
    }
};

これで、この最後の定義を簡単に拡張できるため、型の左辺値と右辺値の両方をFパラメーターにバインドできますf

class G {
    F f_;
public:
    template<typename F>
    G(F&& f) : f_(std::forward<F>(f)) {
       std::cout << "G()" << std::endl;
    }
};

これにより、たとえば、次のようにインスタンスを作成できますG

F f;
G g(f); // Would not be possible with a constructor accepting only rvalues

ただし、この最後のバージョンには注意が必要です。コンストラクターは基本的にコピーコンストラクターとしても機能するため、厄介な状況を回避するために、考えられるすべてのコピーコンストラクターを明示的に定義することをお勧めします。

class G {
    F f_;
public:
    template<typename F>
    G(F&& f) : f_(std::forward<F>(f)) {
       std::cout << "G()" << std::endl;
    }
    G(G const&) = default;
    G(G&); // Must be defaulted out-of-class because of the reference to non-const
};

G::G(G&) = default;

G非テンプレート関数は関数テンプレートのインスタンス化よりも優先されるため、別のオブジェクトからオブジェクトを作成するときにコピーコンストラクターが選択されGます。もちろん、同じことがmoveコンストラクターにも当てはまります。これは演習として残されています。

于 2013-03-09T19:10:14.777 に答える