C++ 17では、保証された戻り値の最適化(RVO)と考えることができるものを介して、std::mutex
などの移動できない(コピーできないものを含む)タイプを返す可能性があります。 簡略化された値による保証されたコピーの省略)カテゴリ :
struct nocopy { nocopy(nocopy&) = delete; nocopy() = default; };
auto getRVO(){
return nocopy();
}
構造化バインディング もあり、次のことが可能になります。
Tuple<T1,T2,T3> f();
auto [x,y,z] = f();
または(ここでも機能の私の理解を使用して コンストラクターのテンプレート引数の推論 )
template<typename T1,typename T2,typename T3>
struct many {
T1 a;
T2 b;
T3 c;
};
// (Original questions missed 'many' on the next line. Thanks, T.C.)
auto f(){ return many{string(),5.7, false} };
auto [x,y,z] = f();
しかし、これらの機能は、このようなことを可能にするように構成されていますか?
auto get_ensured_rvo_str(){
return std::pair(std::string(),nocopy());
}
auto get_class_and_mutex(){
return many{SomeClass(),std::mutex(),std::string()};
}
int main(){
auto rvoStr = get_ensured_rvo_str().first;
auto [ mtx,sc,str ] = get_class_and_mutex();
}
私の考えでは、これが機能するには、std::Tuple
またはmany
を形成するときに、集約コンストラクター引数の保証されたRVOが必要ですが、特に含まれていないRVO(NRVO)という名前ではありません。 P0144R2提案では?
補足:P0144R2は、移動専用タイプがサポートされていることを具体的に述べています。
2.6移動専用タイプ
移動専用タイプがサポートされています。例えば:
struct S { int i; unique_ptr<widget> w; }; S f() { return {0, make_unique<widget>()}; } auto [ my_i, my_w ] = f();
_template<typename T1,typename T2,typename T3> struct many { T1 a; T2 b; T3 c; }; auto f(){ return {string(),5.7, false} };
_
これはコンパイルされません。まず、f
がmany
を返すことだと言ったことはありません。次に、クラステンプレートの引数の推定はコンストラクターで機能し、many
のコンストラクターは、暗黙的に宣言されたデフォルトのコピーおよび移動コンストラクターのみです。
ガイドが必要です:
_template<class T1, class T2, class T3>
many(T1, T2, T3) -> many<T1, T2, T3>;
_
_auto get_ensured_rvo_str(){ return std::pair(std::string(),nocopy()); }
_
これも機能しません。 nocopy()
は、pair
のコンストラクターの参照パラメーターにバインドされた一時的なものに具体化され、コンストラクターはそこから移動しようとして失敗します。その一時的なものを排除することは不可能であり、許可されていません。
(もちろん、Nicol Bolasが彼の回答で指摘しているように、get_ensured_rvo_str().first
でのクラスメンバーアクセスは_get_ensured_rvo_str
_のpair
戻り値を具体化するので、rvoStr
は実際、そのマテリアライズされた一時的なfirst
メンバーから構築されて移動されます。しかし、ここではそのかなり前に問題があります。)
_auto get_class_and_mutex(){ return many{SomeClass(),std::mutex(),std::string()}; } auto [ mtx,sc,str ] = get_class_and_mutex();
_
これは問題ありません(控除ガイドがあると仮定します)。集計の初期化では、many
のコンストラクターは呼び出されません。対応するprvalue初期化子を使用してメンバーを直接初期化します。
構造化バインディング は、個々の値への参照または疑似参照の抽出に基づいて機能するように定義されています。つまり、これを行う場合:
_auto [x,y,z] = f();
_
あなたが得るものはこれのようなものです:
_auto HIDDEN_VALUE = f();
auto &x = get<0>(HIDDEN_VALUE);
auto &y = get<1>(HIDDEN_VALUE);
auto &z = get<2>(HIDDEN_VALUE);
_
構造体を扱う場合、x
、y
、およびz
は参照ではありません。それらは実際の配列メンバーを「参照」するものになりますが、実際の参照ではありません。重要な点は、x
、y
、およびz
は決してコピーではないということです。
そのため、問題は_HIDDEN_VALUE
_がコピーされるかどうかです。そして、_HIDDEN_VALUE
_が価値構築されていることは明らかです。したがって、f()
の戻り値がprvalueである場合、保証された省略のルールが適用されます。
_auto rvoStr = get_ensured_rvo_str().first;
_
式get_ensured_rvo_str()
はprvalueです。ただし、それに_.first
_を適用した結果は、notprvalueになります。 _.first
_を適用すると、(保証されたエリジオンルールの下で)prvalueに一時的なものが作成され、_.first
_が適用されます。抽出された要素(xvalue)は、初期化rvoStr
をコピーするために使用されます。
したがって、標準のどのバージョンでも、rvoStr
へのコピーは省略されません。
_return many{SomeClass(),std::mutex(),std::string()}; ... auto [ mtx,sc,str ] = get_class_and_mutex();
_
return
ステートメントをコンパイルするために必要な追加を行ったと仮定します。
それを考えると、関数の構築は、リターンサイトで_HIDDEN_VALUE
_を直接初期化します。また、アグリゲートの各メンバーはprvaluesによって直接初期化されるため、コピーは行われません。