代码之家  ›  专栏  ›  技术社区  ›  Johan Lundberg

C++17中具有不可移动类型和保证RVO的多个返回值(结构化绑定)

  •  14
  • Johan Lundberg  · 技术社区  · 8 年前

    使用C++17,我们可以返回不可移动(包括不可复制)的类型,例如 std::mutex Guaranteed copy elision through simplified value categories :

    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();
    }
    

    我的想法是,为了使其有效,它需要在形成时保证聚合构造函数参数的RVO std::tuple 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();
    
    2 回复  |  直到 8 年前
        1
  •  10
  •   T.C. Yksisarvinen    8 年前
    template<typename T1,typename T2,typename T3>
    struct many {
      T1 a;
      T2 b;
      T3 c;
    };
    auto f(){ return {string(),5.7, false} };
    

    这无法编译。首先你从没说过 f 是返回一个 many 第二,类模板参数推导适用于构造函数 许多的 是隐式声明的default、copy和move构造函数。

    您需要一个向导:

    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 的构造函数,然后尝试从中移出并失败。不可能也不允许省略该临时语句。

    get_ensured_rvo_str().first 实现了 的返回值 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初始值设定项直接初始化成员。

        2
  •  6
  •   Nicol Bolas    8 年前

    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 已复制。很明显 隐藏_值 是价值构建的。因此,如果 f() 是一个prvalue,则保证省略规则将适用。

    auto rvoStr = get_ensured_rvo_str().first;
    

    表达式 get_ensured_rvo_str() .first 到现在为止 一个prvalue。应用 第一 强制prvalue(在保证的省略规则下)用 第一 正在应用于它。提取的元素是一个xvalue,将用于复制初始化 rvoStr .

    因此,在任何版本的标准下 rvoStr公司

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

    我假设您已经为 return 要编译的语句。

    鉴于此,函数中的构造将直接初始化 隐藏_值 在返回地点。聚合的每个成员都将由prvalues直接初始化,因此不会发生复制。