C++ 为什么按值传递而不是按常量引用传递?

C++ 为什么按值传递而不是按常量引用传递?,c++,function,arguments,constants,C++,Function,Arguments,Constants,因为const引用与通过值传递几乎相同,但没有创建副本(据我所知)。所以有一种情况需要创建变量的副本(所以我们需要使用传递值) 按值传递基本数据类型(如int、float和指针)通常更快 函数可能希望在本地修改参数,而不改变传入变量的状态 C++11引入了移动语义。若要将对象移动到函数参数中,其类型不能为常量引用 在某些情况下,您不修改输入,但仍然需要输入的内部副本,然后还可以按值获取参数。例如,假设有一个函数返回向量的排序副本: template <typename V> V so

因为
const
引用与通过值传递几乎相同,但没有创建副本(据我所知)。所以有一种情况需要创建变量的副本(所以我们需要使用传递值)

  • 按值传递基本数据类型(如int、float和指针)通常更快
  • 函数可能希望在本地修改参数,而不改变传入变量的状态
  • C++11引入了移动语义。若要将对象移动到函数参数中,其类型不能为常量引用

  • 在某些情况下,您不修改输入,但仍然需要输入的内部副本,然后还可以按值获取参数。例如,假设有一个函数返回向量的排序副本:

    template <typename V> V sorted_copy_1(V const & v)
    {
        V v_copy = v;
        std::sort(v_copy.begin(), v_copy.end());
        return v;
    }
    
    template V sorted_copy_1(V const&V)
    {
    V_copy=V;
    排序(v_copy.begin(),v_copy.end());
    返回v;
    }
    
    这很好,但是如果用户有一个向量,他们永远不需要用于任何其他目的,那么您必须在这里创建一个可能不必要的强制副本。因此,只需按值获取参数:

    template <typename V> V sorted_copy_2(V v)
    {
        std::sort(v.begin(), v.end());
        return v;
    }
    
    模板V已排序\u复制\u 2(V)
    {
    排序(v.begin(),v.end());
    返回v;
    }
    
    现在,生成、排序和返回向量的整个过程基本上都可以“就地”完成


    较便宜的例子是使用计数器或迭代器的算法,这些计数器或迭代器需要在算法过程中进行修改。同样,通过值获取这些参数可以直接使用函数参数,而不需要本地副本。

    最好的例子可能是复制和交换习惯用法:

    C& operator=(C other)
    {
        swap(*this, other);
        return *this;
    } 
    
    通过值而不是常量引用获取
    other
    ,可以更轻松地编写正确的赋值运算符,从而避免代码重复并提供强大的异常保证

    此外,通过值传递迭代器和指针,因为这样可以使这些算法更合理地编码,因为它们可以在本地修改参数。否则,像这样的东西无论如何都必须立即复制它的输入,这既低效又愚蠢。我们都知道,避免使用看似愚蠢的代码是首要任务:

    template<class BidirIt, class UnaryPredicate>
    BidirIt partition(BidirIt first, BidirIt last, UnaryPredicate p)
    {
        while (1) {
            while ((first != last) && p(*first)) {
                ++first;
            }
            if (first == last--) break;
            while ((first != last) && !p(*last)) {
                --last;
            }
            if (first == last) break;
            std::iter_swap(first++, last);
        }
        return first;
    }
    
    模板
    BidirIt分区(BidirIt first,BidirIt last,一元谓词p)
    {
    而(1){
    而((第一个!=最后一个)和&p(*第一个)){
    ++第一,;
    }
    如果(第一次==最后一次--)中断;
    while((first!=last)和&!p(*last)){
    --最后;
    }
    如果(第一次==最后一次)中断;
    标准:国际热核聚变实验堆交换(第一次++,最后一次);
    }
    先返回;
    }
    
    A
    const&
    如果没有
    const\u cast
    通过引用,则无法更改,但可以更改。在代码离开编译器“分析范围”的任何时候(可能是对不同编译单元的函数调用,或者通过函数指针,它无法在编译时确定的值),必须假定引用的值可能已更改

    这需要优化成本。而且,这会使您更难对代码中可能存在的bug或怪癖进行推理:引用是非本地状态,而只在本地状态下运行且不会产生副作用的函数很容易推理。使您的代码易于推理是一大好处:维护和修复代码所花费的时间比编写代码所花费的时间要多,而且在性能上所花费的精力是可以替代的(您可以将其花在重要的地方,而不是在任何地方的微优化上浪费时间)

    另一方面,一个值需要将该值复制到本地自动存储中,这是有成本的

    但是,如果您的对象复制成本很低,并且您不希望出现上述效果,请始终按值执行,因为这会使编译器更容易理解函数

    当然,只有在复制价值便宜的情况下。如果复制成本很高,或者即使复制成本未知,该成本应该足以由
    const&
    承担

    上面的简短版本:按值取值使您和编译器更容易推断参数的状态

    还有另外一个原因。如果您的对象移动成本很低,而且您无论如何都要存储一个本地副本,那么按值获取将提高效率。如果通过
    const&
    获取
    std::string
    ,然后制作本地副本,可以创建一个
    std::string
    ,以传递该参数,并为本地副本创建另一个

    如果按值获取
    std::string
    ,则只会创建一个副本(可能还会移动)

    举个具体例子:

    std::string some_external_state;
    void foo( std::string const& str ) {
      some_external_state = str;
    }
    void bar( std::string str ) {
      some_external_state = std::move(str);
    }
    
    然后我们可以比较:

    int main() {
      foo("Hello world!");
      bar("Goodbye cruel world.");
    }
    
    调用
    foo
    将创建一个
    std::string
    ,其中包含
    “Hello world!”
    。然后将其再次复制到
    某些外部\u状态
    。复制2份,丢弃1个字符串

    bar
    的调用直接创建
    std::string
    参数。然后将其状态移动到
    某些外部状态
    。创建1个副本,移动1个,丢弃1个(空)字符串

    由于任何分配都发生在
    bar
    之外,而
    foo
    可能引发资源耗尽的异常,因此该技术还带来了某些异常安全改进

    这仅适用于完美转发令人讨厌或失败、移动成本低、复制成本高以及几乎肯定要制作参数的本地副本的情况


    最后,还有一些小类型(如
    int
    ),直接拷贝的非优化ABI比
    常量和
    参数的非优化ABI快。当编码不能或不会优化的接口时,这一点很重要,通常是一种微观优化。

    像许多事情一样,这是一种平衡

    我们通过常量引用传递给