代码之家  ›  专栏  ›  技术社区  ›  DeusAduro

通过const-ref传递整型有什么好处

  •  9
  • DeusAduro  · 技术社区  · 15 年前

    问题:通过常量引用传递整型是否有好处,而不是简单的按值传递。

    IE.

    void foo(const int& n); // case #1
    

    虚拟

    void foo(int n); // case #2
    

    对于用户定义的类型,答案很清楚,case 1避免了不必要的复制,同时确保了对象的一致性。但是在上面的例子中,引用和整数(至少在我的系统中)的大小是相同的,所以我无法想象在函数调用所需的时间(由于复制)方面会有很大的差异。但是,我的问题实际上与内嵌函数的编译器有关:

    对于非常小的内联函数,编译器是否必须在case 2中复制整数?通过让编译器知道我们不会更改引用,它是否可以在不必要复制整数的情况下内联函数调用?

    欢迎任何建议。

    12 回复  |  直到 10 年前
        1
  •  12
  •   Michael Burr    15 年前

    通过const-ref传递一个内置int类型实际上是一个小的去优化(通常)。至少对于非内联函数。编译器可能必须实际传递一个指针,该指针必须被取消引用才能获取值。您可能认为它可以一直优化这一点,但别名规则和支持单独编译的需要可能会迫使编译器手下留情。

    但是,对于第二个问题:

    对于非常小的内联函数,编译器是否必须在case 2中复制整数?通过让编译器知道我们不会更改引用,它是否可以在不必要复制整数的情况下内联函数调用?

    如果语义允许,编译器应该能够优化掉副本或取消引用,因为在这种情况下,编译器完全了解调用站点的状态和函数实现。它很可能只是将该值加载到一个寄存器中,并在完成参数的处理后将该寄存器用于其他用途。当然,所有这些都非常依赖于函数的实际实现。

        2
  •  7
  •   Laurence Gonsalves    15 年前

    这取决于编译器,但我希望任何合理的优化器都能以任何方式给您提供相同的结果。

    我用GCC进行了测试,结果确实是一样的。这是我测试的代码:

    inline int foo(const int& n) {
      return n * 2;
    }
    
    int bar(int x) {
      int y = foo(x);
      return y;
    }
    

    (有或无const&on foo的n参数)

    然后我用GCC4.0.1编译了以下命令行:

    g++ -O3 -S -o foo.s foo.cc
    

    两个编译的输出是相同的。

        3
  •  6
  •   Naveen    15 年前

    实际上,当有人对基本数据类型使用像这样的常量引用时,我会感到恼火。我看不出这样做有什么好处,尽管对于大于 sizeof(pointer) 它可能更有效。不过,我真的不在乎这样一分钟的“优化”。

        4
  •  4
  •   rlbond    15 年前

    这通常不值得。即使对于内联函数,编译器也不会是愚蠢的。唯一一次我会说这是适当的是如果你有一个模板;它可能不值得额外的努力专门为内置只采取一个副本而不是一个参考。

        5
  •  2
  •   asveikau    15 年前

    很多人都说这两者没有区别。我可以看到一个(也许是人为的)情况,在这种情况下,差异将很重要…

    int i = 0;
    
    void f(const int &j)
    {
        i++;
    
        if (j == 0)
        {        
            // Do something.
        }
    }
    
    void g()
    {
        f(i);
    }
    

    但是…正如其他人提到的…整数和指针的大小可能相似。对于小到整数的对象,引用会降低性能。可能不会 值得注意的是,除非你的方法被称为很多,但它会存在。另一方面,在某些情况下,编译器可能会优化它。

        6
  •  2
  •   comingstorm    15 年前

    在编写或使用模板时,可能会以(const int&)结尾,因为模板编写器不知道实际类型是什么。如果对象是重量级的,则传递引用是正确的操作;如果对象是int或其他类型的对象,则编译器可能能够对其进行优化。

    在缺乏某种外部需求的情况下,通常没有理由为一次性函数做这样的事情——它只是额外的类型,加上抛出引用实际上会抑制优化。在寄存器中复制小数据要比在发生变化时从内存中重新加载要便宜得多!

        7
  •  2
  •   denisenkom    15 年前

    你可以使用 boost::call_traits<your type>::param_type 最佳参数传递。它默认为基本类型的简单参数传递和结构和类的常量引用传递。

        8
  •  1
  •   R Samuel Klatchko    15 年前

    我想不出有什么好处。我甚至看到过这样的建议:在编写模板时,使用元编程按值传递整型,并且只对非整型使用常量引用。

        9
  •  1
  •   3yE    15 年前

    不要这样做。 int 与公共32位平台上的指针/引用大小相同,并且 更小的 在64位上,因此您可以让自己处于性能劣势而不是优势。我的意思是,所有的函数参数都按顺序被推到堆栈上,这样函数就可以读取它们,在引用的情况下,它要么是您的int,要么是它的地址。另一个缺点是被叫方将访问 n 通过间接寻址(取消对地址的引用),或者将在其堆栈上进行复制作为优化。

    如果您对一个按值传递的int做了一些更改,那么它可能被写回堆栈上传递它的位置,或者写回一个新的堆栈位置。第二种情况自然不是有利的,但不应该发生。通过解释你禁止自己做出这样的改变,但这同样适用于 const int .

    在适当的内联情况下,这当然无关紧要,但请记住,并非您编写内联的所有内容都将是这样。

        10
  •  0
  •   John Leidegren    15 年前

    引用的成本通常与整型的成本相同,但是对于引用,必须进行间接寻址,因为对某些内存的引用必须解析为一个值。

    只需按值复制,对于内置类型坚持不变的约定。

        11
  •  0
  •   tony    15 年前

    请阅读 Want Speed? Pass by Value 戴夫·亚伯拉罕。

        12
  •  0
  •   BenMorel Sonaten    10 年前

    不仅仅是表演。 一个真实的故事:本周,我注意到一位同事试图改进数字食谱,并替换了宏。

    
        #define SHFT(a,b,c,d) do { (a)=(b); (b)=(c); (c)=(d); } while (0)
    

    通过这个函数

    
        inline void Rotate(double& dFirst, double& dSecond, double& dThird, const double dNewValue)
        {
            dFirst = dSecond;
            dSecond = dThird;
            dThird = dNewValue;
        }   // Function Rotate
    

    如果他通过引用传递最后一个参数的话,这是可行的,但实际上,这段代码

     
            Rotate(dum,*fb,*fa,dum);
    

    本来应该交换FA和FB的,现在不起作用了。 不带常量的引用传递它是不可能的,因为在其他地方,非l值传递给最后一个参数。