1

誰もが知っているかもしれませんが、右辺値参照を参照折りたたみルールと組み合わせて使用​​して、次のように完全な転送関数を構築しています。

template<typename T>
void f(T&& arg) {
    otherfunc(std::forward<T>(arg));
}

f(4);

参照折りたたみルールは次のようになります

+------+-----+--------+
| T    | Use | Result |
|------|--------------|
| X&   | T&  | X&     |
| X&   | T&& | X&     |
| X&&  | T&  | X&     |
| X&&  | T&& | X&&    |
+------+-----+--------+

したがって、私の例fでは、Tisint&&T&&is がint&& &&に折りたたまれint&&ます。

T私の質問は、が既に推定されている場合、なぜこれらのルールが必要なのint&&ですか? なぜだろう

template<typename T>
void f(T arg);

f(4);

if isvoid f(int)の代わりvoid f(int&&)に? もしが 本当にでが であり であり、したがってである場合、参照の折りたたみルールが適用されないように見えるのに、なぜ参照の折りたたみルールが必要なのですか? これらは私の限られた知識から判断できる唯一の 2 つのオプションなので、明らかに私が知らないルールがあります。Tint&&TintT&&int&&void f(int&&)

これについての標準からの引用を見ることも役に立ちます。

4

2 に答える 2

1

あなたの例でTint、ではありませんint&&void f(T arg)バリアントは常に値によってパラメーターを受け入れ、コピーを作成します。もちろん、それは完全な転送のポイントを無効にotherfuncします。コピーを回避して、参照によってパラメーターを取得することは非常にうまくいく可能性があります。コピーできないクラスで呼び出すこともできます。

一方、f()左辺値、たとえば class を渡すとしますC。すると、 にTなり、 に崩壊します。このように、完全な転送は、いわば「左辺値性」を保持します。それが崩壊ルールの目的です。C&T&&C& &&C&

検討:

#include <utility>
#include <iostream>
using namespace std;

class C {
public:
    C() : x(0) {}
    int x;
private:
    C(const C&);
};

void otherfunc(C& c) { c.x = 1; }

template<typename T>
void f(T&& arg) {
    otherfunc(std::forward<T>(arg));
}

template<typename T>
void g(T arg) {
    otherfunc(std::forward<T>(arg));
}

int main() {
    C c;
    f(c);  // OK
//  g(c);  // Error: copy constructor inaccessible

    cout << c.x;  // prints 1
    return 0;
}
于 2013-10-05T03:50:50.987 に答える
1

私の質問は、T が既に int&& に推定されている場合、なぜこれらの規則が必要なのですか?

これは正しくありません。型推定の規則では、引数が参照であると推定されません。つまり、次のとおりです。

template <typename T>
void f(T);

そして式:

X g();
X& h();
X a;
f(g());        // argument is an rvalue, cannot be bound by lvalue-ref
f(h());        // argument is an lvalue
f(a);          // argument is an lvalue

推定される型はX最後の 2 つのケースであり、最初のケースではコンパイルに失敗します。推定される型は、参照型ではなく値型になります。

次のステップは、テンプレートが左辺値または右辺値参照によって引数を取った場合に、推定される型がどうなるかを理解することです。左辺値参照の場合、オプションは明確で、変更されたf:

template <typename T>
void f(T &);

f(g());       // only const& can bind an rvalue: f(const X&), T == const int
f(h());       // f(X&)
f(a);         // f(X&)

ここまでは、以前のバージョンの標準ですでに定義されていました。問題は、テンプレートが右辺値参照を取る場合、推定される型はどうあるべきかということです。これは C++11 で追加されたものです。今考えてみましょう:

template <typename T>
void f(T &&);

また、右辺値は右辺値にのみバインドされ、左辺値にはバインドされません。これは、左辺値参照の場合と同じ単純なルール (型 T が呼び出しをコンパイルするもの) を使用すると、2 番目と 3 番目の呼び出しがコンパイルされないことを意味します。

f(g());     // Fine, and rvalue-reference binds the rvalue
f(h());     // an rvalue-reference cannot bind an lvalue!
f(a);       // an rvalue-reference cannot bind an lvalue!

参照折りたたみルールがなければ、ユーザーはテンプレートに 2 つのオーバーロードを提供する必要があります。1 つは右辺値参照を受け取り、もう 1 つは左辺値参照を受け取ります。問題は、引数の数が増えると代替の数が指数関数的に増加し、C++03 で完全な転送を実装するのがほぼ同じくらい難しくなることです (右辺値参照で右辺値を検出できるという唯一の利点があります)。

そのため、別のことを行う必要があります。それが参照の崩壊です。これは、目的のセマンティクスを実際に記述する方法です。それらを説明する別の方法は&&、テンプレート引数で入力する場合、実際にはrvalue-referenceを要求しないということです。これは、 lvalueでの呼び出しを許可しないためです。むしろ、コンパイラーに最善の結果を提供するように求めています。参照一致のタイプ。

于 2013-10-05T03:52:45.733 に答える