【问题标题】:Efficiency of construction of stl container based on a function return基于函数返回的stl容器构造效率
【发布时间】:2012-12-24 10:22:39
【问题描述】:

我有一个返回 stl 容器的工厂函数:

const std::vector<int> f(...) {
    std::vector<int> retval;
    return retval;
}

我想可以如下定义一个stl实例(没有错误):

const std::vector<int> stl_instance(f(...));

但是这样做有效率吗?

临时stl对象是直接赋值给stl_instance的吗?

【问题讨论】:

    标签: c++ c++11 rvalue-reference move-semantics return-value-optimization


    【解决方案1】:

    返回 const rvalue 是 C++11 中的反模式。首先考虑返回非常量右值:

    std::vector<int> f(int n) 
    { 
        return std::vector<int>(n); 
    }
    
    int main() 
    { 
        std::vector<int> v; 
        v = f(3); 
    } 
    

    在 C++98/03 中,这段代码至少会进入堆两次:

    1. 在 f 内创建向量(如果适用 RVO)
    2. 将 f 的返回分配给 v。

    如果您的编译器不应用 RVO,您将获得 3 个堆分配。

    在 C++11 中,您只会获得 1 个堆分配:用于在 f 内部创建向量。无论 RVO 如何,都会发生这种情况。原因是所有的 STL 容器都有带有签名的移动构造函数和移动赋值运算符

    vector( vector&& other );
    vector& operator=( vector&& other );
    

    右值引用&amp;&amp; 会将资源从您的创建函数内部直接移动到它们的目的地。但是,您的代码具有签名

    const std::vector<int> f(int n) 
    { 
        return std::vector<int>(n); 
    }
    

    将禁用移动语义,因为 T&&(即移动构造函数和赋值运算符的参数)不会绑定到 const rvalue 参数(即函数的返回值)。这有效地使您的代码在 C++98/03 下运行(即具有 2 或 3 个堆分配)。

    【讨论】:

      【解决方案2】:

      这是很好的合法代码。
      编译器将通过复制省略处理必要的优化(如果可以)。

      临时的f(...) 保证至少在表达式结束之前存在。请注意,表达式在从stl_instance(f(...)) 返回之后结束,准确地说是;调用结束时的分号)。所以它是完全有效的。

      C++03 标准 §12.2/3:

      临时对象在评估完整表达式 (1.9) 的最后一步时被销毁,该完整表达式 (从词法上) 包含它们的创建点。

      【讨论】:

      • @AlokSave 这是合法的,但肯定不是很好的 C++11 代码,因为 const 返回值禁用移动语义。
      • @KarthikT Move 语义被 const 返回值禁用。
      猜你喜欢
      • 1970-01-01
      • 2021-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-21
      • 2016-09-16
      • 2014-08-22
      • 1970-01-01
      相关资源
      最近更新 更多