6

次のように、関数テンプレートが非 const 左辺値の場合に左辺値性を維持しながら引数を転送する必要があるが、それ自体は引数が実際に何であるかにとらわれない状況を考えてみましょう。

template <typename T>
void target(T&) {
    cout << "non-const lvalue";
}

template <typename T>
void target(const T&) {
    cout << "const lvalue or rvalue";
}


template <typename T>
void forward(T& x) {
    target(x);
}

xが右辺値の場合T、定数型に推定されるのではなく、エラーが発生します。

int x = 0;
const int y = 0;

forward(x); // T = int
forward(y); // T = const int
forward(0); // Hopefully, T = const int, but actually an error
forward<const int>(0); // Works, T = const int

右辺値を (明示的forwardなテンプレート引数を呼び出さずに) 処理するには、forward(const T&)本体が完全に複製されていても、オーバーロードが必要なようです。

この重複を避ける方法はありますか?

4

4 に答える 4

5

これは既知の問題であり、C++0x での右辺値参照の目的です。この問題は、C++03 では一般的な解決策がありません。

これが発生するのには、非常に無意味な古風な歴史的理由があります。一度尋ねたのを覚えていますが、その答えは私を大いに落ち込ませました。

于 2011-01-02T13:40:11.970 に答える
2

x右辺値はいつですか

しかしx、名前は左辺値であるため、決して右辺値ではありません。

この重複を避ける方法はありますか?

C++0x には次の方法があります。

#include <utility>

template <typename T>
void forward(T&& x)   // note the two ampersands
{
    target(std::forward<T>(x));
}

参照の折りたたみ規則のおかげで、式std::forward<T>(x)は独自の前方関数への引数と同じ値カテゴリになります。

于 2011-01-02T13:43:11.627 に答える
2

一般に、変数が const であるかどうか、または参照であるかどうかのセマンティクスはまったく異なるため、テンプレートに関するこの厄介な問題には重複が必要です。

これに対する C++11 の解決策は「decltype」ですが、それが行うのは複合型であり、すでに壊れている型システムであるため、これは悪い考えです。

標準または委員会が何と言おうと、「const int」はタイプではなく、決してタイプになることはありません。また、「int&」が型になることもありません。したがって、テンプレートの型パラメーターは、そのような非型へのバインドを許可されるべきではありません。ありがたいことに、これは当てはまります。残念ながら、この無原則な置換を明示的に強制することはできます。

「const const int」を「const int」に縮小するなど、この問題を「修正」しようとするばかげたルールがいくつかあります。「int & &」を取得した場合に何が起こるかさえわかりません。 「int&」をタイプとしてカウントしないでください。「int 左辺値」タイプがありますが、それは異なります。

int x;       // type is lvalue int
int &y = x;  // type is lvalue int

この問題に対する正しい解決策は、実際には非常に単純です。すべてが変更可能なオブジェクトです。"const" を破棄し (それほど有用ではありません)、参照、左辺値、および右辺値を破棄します。右辺値かどうかにかかわらず、すべてのクラス型がアドレス指定可能であることは明らかです ("this" ポインターはアドレスです)。委員会が右辺値への代入とアドレス指定を禁止しようとした無駄な試みがありました。アドレス指定のケースは機能しますが、些細なキャストで簡単にエスケープされます。代入のケースはまったく機能しません (代入はメンバー関数であり、右辺値は非定数であるため、いつでもクラス型付き右辺値に代入できます)。

とにかく、テンプレートのメタプログラミング群集には「decltype」があり、「const」ビットと「&」ビットを含む宣言のエンコーディングを見つけることができ、さまざまなライブラリ演算子を使用してそのエンコーディングを分解できます。その情報は実際にはタイプ情報ではないため、これは以前は実行できませんでした (「ref」は実際にはストレージ割り当て情報です)。

于 2011-01-02T14:39:57.547 に答える
0

&私が理解しているように、k個の引数があると仮定すると、C++03での唯一の「解決策」は、とconst&パラメータの可能なすべての組み合わせを取る 2^k 転送関数を手動で書き出すことです。target()説明のために、実際に 2 つのパラメーターを受け取ったと想像してください。その場合、次のものが必要になります。

template <typename T>
void forward2(T& x, T& y) {
    target(x, y);
}

template <typename T>
void forward2(T& x, T const& y) {
    target(x, y);
}

template <typename T>
void forward2(T const& x, T& y) {
    target(x, y);
}

template <typename T>
void forward2(T const& x, T const& y) {
    target(x, y);
}

明らかに、これは大きな k の場合は非常に扱いにくくなるため、他の回答が述べたように C++0x で右辺値を参照します。

于 2011-01-02T14:33:57.067 に答える