With C++ 17, we will have the possibility to return unmovable (including uncopyable) types such as std::mutex
, via what can be thought of as guaranteed return value optimization (RVO): Guaranteed copy elision through simplified value categories:
struct nocopy { nocopy(nocopy&) = delete; nocopy() = default; };
auto getRVO(){
return nocopy();
}
We will also have structured bindings, allowing:
tuple<T1,T2,T3> f();
auto [x,y,z] = f();
or (here also using my understanding of the feature template argument deduction for constructors)
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();
But do these features compose to enable something like this?
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();
}
My thinking is that for this to work, it would required guaranteed RVO of the aggregate constructor arguments at forming std::tuple
or many
, but wouldn't that be named RVO (NRVO) which is specifically not included in the P0144R2 proposal?
Side note: P0144R2 specifically mentions that move-only types are supported:
2.6 Move-only types
Move-only types are supported. For example:
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} };
This won't compile. First you never said f
is to return a many
. Second, class template argument deduction work with constructors, and the only constructors of many
are the implicitly declared default, copy and move constructors.
You need a guide:
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()); }
This doesn't work either. nocopy()
is materialized into a temporary that is bound to the reference parameter of pair
's constructor, which then attempts to move from it and fails. No elision of that temporary is possible or allowed.
(Of course, as Nicol Bolas points out in his answer, the class member access in get_ensured_rvo_str().first
materializes the pair
return value of get_ensured_rvo_str
, so rvoStr
would in fact be moved constructed from the first
member of that materialized temporary. But here you have a problem well before that.)
auto get_class_and_mutex(){ return many{SomeClass(),std::mutex(),std::string()}; } auto [ mtx,sc,str ] = get_class_and_mutex();
This is fine (assuming you have a deduction guide). Aggregate initialization doesn't call any constructor of many
; it initializes the members directly with the corresponding prvalue initializer.