これは、 C++0x右辺値参照と一時的なものに対する後続の質問 です。
前の質問で、このコードがどのように機能するかを尋ねました。
void f(const std::string &); //less efficient
void f(std::string &&); //more efficient
void g(const char * arg)
{
f(arg);
}
暗黙の一時的なもののために移動オーバーロードを呼び出す必要があるようです。これはGCCでは発生しますが、MSVC(またはMSVCのIntellisenseで使用されるEDGフロントエンド)では発生しません。
このコードはどうですか?
void f(std::string &&); //NB: No const string & overload supplied
void g1(const char * arg)
{
f(arg);
}
void g2(const std::string & arg)
{
f(arg);
}
以前の質問への回答に基づくと、機能g1
は合法であるようです(GCC 4.3-4.5では受け入れられますが、MSVCでは受け入れられません)。g2
ただし、 13.3.3.1.4 / 3節では、左辺値が右辺値のref引数にバインドできないため、GCCとMSVCはどちらも拒否します。私はこの背後にある理論的根拠を理解しています-それはN2831「右辺値参照による安全性の問題の修正」で説明されています。また、GCCの元のパッチは著者の1人(Doug Gregor)によって書かれたため、GCCはおそらくその論文の著者が意図したとおりにこの条項を実装していると思います。
しかし、これは非常に直感的ではありません。私にとって、(a)aconst string &
は概念的にastring &&
よりもaconst char *
に近く、(b)コンパイラは、次のg2
ように記述されているかのように、に一時的な文字列を作成できます。
void g2(const std::string & arg)
{
f(std::string(arg));
}
実際、コピーコンストラクターは暗黙の変換演算子と見なされる場合があります。構文的には、これはコピーコンストラクターの形式で提案され、標準では13.3.3.1.2/4節で具体的に言及されています。ここでは、派生ベース変換のコピーコンストラクターに他のユーザー定義よりも高い変換ランクが与えられます。変換:
クラスタイプの式の同じクラスタイプへの変換には完全一致ランクが与えられ、クラスタイプの式のそのタイプの基本クラスへの変換には、コピー/移動にもかかわらず、変換ランクが与えられます。これらの場合、コンストラクター(つまり、ユーザー定義の変換関数)が呼び出されます。
void h(Base)
(これは、派生クラスを、基本クラスを値で受け取るのような関数に渡すときに使用されると思います。)
動機
これを尋ねる私の動機は、新しいc ++ 0x右辺値参照演算子のオーバーロードを追加するときに冗長コードを減らす方法(「新しいc ++ 0x右辺値参照演算子のオーバーロードを追加するときに冗長コードを減らす方法」)で尋ねられた質問のようなものです。
移動可能な可能性のある多数の引数を受け入れる関数があり、可能な場合はそれらを移動し(たとえば、ファクトリ関数/コンストラクターObject create_object(string, vector<string>, string)
など)、必要に応じて各引数を移動またはコピーする場合は、すぐに記述を開始します。たくさんのコード。
引数の型が移動可能である場合は、上記のように、値で引数を受け入れる1つのバージョンを記述できます。ただし、引数がC ++ 03の(レガシー)移動不可であるが交換可能なクラスであり、それらを変更できない場合は、右辺値参照のオーバーロードを作成する方が効率的です。
したがって、左辺値が暗黙的なコピーを介して右辺値にバインドされた場合、次のようなオーバーロードを1つだけ記述することができcreate_object(legacy_string &&, legacy_vector<legacy_string> &&, legacy_string &&)
、右辺値と左辺値の参照オーバーロードのすべての組み合わせを提供するように機能します-左辺値であった実際の引数はコピーされてからバインドされます引数には、右辺値である実際の引数が直接バインドされます。
明確化/編集:これは、C ++ 0x std::stringやstd::vectorのような可動型の値による引数の受け入れと実質的に同じであることに気付きました(moveコンストラクターが概念的に呼び出される回数を保存します)。ただし、コピー可能であるが移動不可能なタイプの場合は同じではありません。これには、明示的に定義されたコピーコンストラクターを持つすべてのC++03クラスが含まれます。この例を考えてみましょう。
class legacy_string { legacy_string(const legacy_string &); }; //defined in a header somewhere; not modifiable.
void f(legacy_string s1, legacy_string s2); //A *new* (C++0x) function that wants to move from its arguments where possible, and avoid copying
void g() //A C++0x function as well
{
legacy_string x(/*initialization*/);
legacy_string y(/*initialization*/);
f(std::move(x), std::move(y));
}
g
を呼び出すf
と、x
とがコピーされy
ます-コンパイラがそれらを移動する方法がわかりません。f
代わりに引数を取ると宣言された場合、呼び出し元が引数に対してlegacy_string &&
明示的に呼び出したコピーを回避できstd::move
ます。これらがどのように同等であるかわかりません。
質問
私の質問は次のとおりです。
- これは標準の有効な解釈ですか?とにかく、それは従来のものでも意図されたものでもないようです。
- 直感的に理解できますか?
- 私が見ていないというこの考えに問題はありますか?それが正確に予期されていないときにコピーが静かに作成される可能性があるようですが、それはとにかくC ++ 03の場所の現状です。また、それはいくつかを作りますオーバーロードは現在実行可能ではないときに実行可能ですが、実際には問題になるとは思いません。
- これは、GCCの実験的なパッチなどを作成する価値があるほど重要な改善ですか?