在C ++中传递值或传递常量引用会更好吗?

在C ++中传递值或传递常量引用会更好吗?

我想知道哪种更好的做法。 我意识到通过常量引用应该提供更好的性能,因为你没有制作变量的副本。


对于迭代器和函数对象 (lambdas,派生自std::*_function),除了内建类型( charintdouble等)之外,对于所有类型使用const ref,通常建议使用最佳做法1 )。

在移动语义存在之前尤其如此。 原因很简单:如果按值传递,必须创建一个对象的副本,除了非常小的对象外,这通常比传递引用更昂贵。

使用C ++ 11,我们获得了移动语义。 简而言之,移动语义允许在某些情况下,对象可以“按值”传递而不需要复制它。 特别是当你传递的对象是右值时,就是这种情况。

移动物体本身至少与通过参考一样昂贵。 但是,在很多情况下,函数会在内部复制一个对象 - 即它将获得参数的所有权

在这些情况下,我们有以下(简化)权衡:

  • 我们可以通过引用传递对象,然后在内部复制。
  • 我们可以通过价值传递对象。
  • “按值传递”仍然会导致对象被复制,除非对象是右值。 在右值的情况下,可以移动对象,以便第二种情况突然不再是“复制,然后移动”,而是“移动,然后(可能)再次移动”。

    对于实现合适的移动构造函数(如向量,字符串...)的大对象,第二种情况比第一种效率高得多。 因此, 如果函数拥有参数的所有权,并且对象类型支持高效移动 ,则建议使用值传递


    历史笔记:

    实际上,任何现代编译器都应该能够计算出传值时的代价是昂贵的,并且如果可能的话,隐式地将调用转换为使用const ref。

    理论上。 在实践中,编译器不能总是在不破坏函数的二进制接口的情况下改变它。 在某些特殊情况下(函数内联时),如果编译器可以发现原始对象不会通过函数中的操作进行更改,则实际上将复制副本。

    但是总的来说,编译器无法确定这一点,并且C ++中移动语义的出现使得这种优化更不相关。


    1例如Scott Meyers,Effective C ++。

    2这对于对象构造函数来说尤其如此,这可能需要参数并将它们内部存储为构造对象状态的一部分。


    编辑:由cave-next上的Dave Abrahams撰写的新文章:

    想要速度? 按价值传递。


    按值传递复制价格低廉的结构时,编译器可能会认为这些对象不是别名(不是相同的对象)。 使用传递引用编译器不能总是假设。 简单的例子:

    foo * f;
    
    void bar(foo g) {
        g.i = 10;
        f->i = 2;
        g.i += 5;
    }
    

    编译器可以优化它

    g.i = 15;
    f->i = 2;
    

    因为它知道f和g不共享相同的位置。 如果g是一个引用(foo&),编译器就不可能假设这一点。 因为gi可能会被f-> i替代,并且必须具有值7.因此编译器将不得不从内存中重新获取gi的新值。

    对于更多实用的规则,这里有一些在Move Constructors文章中发现的一系列规则(强烈推荐阅读)。

  • 如果该函数试图将参数更改为副作用,请使用非const引用。
  • 如果该函数不修改其参数并且该参数是基本类型,则可以按值取值。
  • 除非在以下情况下,否则请通过const引用
  • 如果该函数无论如何都需要复制const引用,则可以通过值来获取它。
  • 上面的“Primitive”意味着基本上只有几个字节长的小数据类型,并且不是多态的(迭代器,函数对象等等)或者复制成本高。 在那篇文章中,还有一条规则。 这个想法是,有时候想要做一个副本(如果参数不能被修改),有时候不想要(如果参数本来就是临时的,那么在函数中想要使用参数本身) , 例如)。 该文件详细解释了如何做到这一点。 在C ++ 1x中,该技术可以与语言支持一起使用。 在此之前,我会遵循上述规则。

    例子:为了使字符串大写并返回大写版本,应该总是按值传递:无论如何,必须获取它的副本(不能直接更改const引用) - 所以最好使其尽可能透明呼叫者并尽早复制,以便呼叫者尽可能优化 - 如该纸中所述:

    my::string uppercase(my::string s) { /* change s and return it */ }
    

    但是,如果您不需要更改参数,请参阅const:

    bool all_uppercase(my::string const& s) { 
        /* check to see whether any character is uppercase */
    }
    

    但是,如果参数的目的是将参数写入参数,则通过非const参考传递参数

    bool try_parse(T text, my::string &out) {
        /* try to parse, write result into out */
    }
    

    取决于类型。 您将增加必须作参考和取消引用的小额开销。 对于大小等于或小于使用默认复制ctor的指针的类型,按值传递可能会更快。

    链接地址: http://www.djcxy.com/p/73007.html

    上一篇: Is it better in C++ to pass by value or pass by constant reference?

    下一篇: Different types of *