14

使用 C++ 17,我们将有可能返回不可移动(包括不可复制)类型,例如std::mutex,通过可以被认为是保证返回值优化 (RVO):通过简化值类别保证复制省略

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::tupleor时保证聚合构造函数参数的 RVO many,但不会将其命名为 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();
4

2 回答 2

10
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返回值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();

这很好(假设你有一个扣除指南)。聚合初始化不调用任何构造函数many;它直接使用相应的prvalue初始化器初始化成员。

于 2016-07-14T23:15:40.717 回答
6

结构化绑定被定义为在提取对单个值的引用或伪引用的基础上工作。也就是说,如果你这样做:

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, andz不会是引用;它们将是“引用”实际数组成员的东西,但它不是实际引用。要点是x,y并且z永远不是任何东西的副本

因此,问题是是否HIDDEN_VALUE被复制。很明显,这HIDDEN_VALUE是价值构建的。因此,如果 的返回f()是纯右值,则保证省略规则将适用。

auto rvoStr = get_ensured_rvo_str().first;

表达式get_ensured_rvo_str()是纯右值。但是,应用.first到它的结果不是prvalue。应用.first强制prvalue(在保证省略规则下)构造一个临时的,并.first应用于它。提取的元素是一个 xvalue,将用于复制 initialize rvoStr

因此,在任何版本的标准下,副本都被rvoStr省略了。

return many{SomeClass(),std::mutex(),std::string()};
...
auto [ mtx,sc,str ] = get_class_and_mutex();

我将假设您已经return为编译语句进行了必要的添加。

鉴于此,函数中的构造将直接HIDDEN_VALUE在返回站点初始化 。并且聚合的每个成员都将直接由纯右值初始化,因此不会发生复制。

于 2016-07-14T23:20:29.260 回答