使用C ++ 17,我们可以通过可以被认为是保证返回值优化(RVO)的方式返回不可移动的(包括不可复制的)类型,例如std::mutex
:Guaranteed copy elision through simplified value categories:< / p>
struct nocopy { nocopy(nocopy&) = delete; nocopy() = default; };
auto getRVO(){
return nocopy();
}
我们还会structured bindings,允许:
tuple<T1,T2,T3> f();
auto [x,y,z] = f();
或(此处也使用我对功能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();
但这些功能是否可以实现这样的功能呢?
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();
答案 0 :(得分:9)
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
中的班级成员访问权限实现了pair
的{{1}}返回值,因此get_ensured_rvo_str
会在事实是从物化临时的rvoStr
成员构建的。但是在这之前你有一个问题。)
first
这很好(假设您有扣除指南)。聚合初始化不会调用auto get_class_and_mutex(){
return many{SomeClass(),std::mutex(),std::string()};
}
auto [ mtx,sc,str ] = get_class_and_mutex();
的任何构造函数;它使用相应的prvalue初始化程序直接初始化成员。
答案 1 :(得分:6)
Structured binding被定义为在提取对单个值的引用或伪引用的基础上工作。也就是说,如果你这样做:
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
应用于它的结果是不是 prvalue。应用.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直接初始化,因此不会发生复制。