我应该通过引用或值来引用内联函数的参数吗?

时间:2009-04-06 16:44:20

标签: c++ inline

其中一个更快吗?

inline int ProcessByValue(int i)
{
    // process i somehow
}

inline int ProcessByReference(const int& i)
{
    // process i somehow
}

我知道整数类型应该按值传递。但是,我担心编译器可能会内联ProcessByValue以包含副本。对此有规定吗?

9 个答案:

答案 0 :(得分:25)

它没有任何区别。在这两种情况下,代码都将内联到相同的内容。不必要地复制int(以pass-by-value)将被编译器消除,并且不必要地创建对int的引用,并且在访问int时跟随该间接层也将被消除。

你的问题似乎是基于一些错误的假设:

  • 内联关键字实际上会使您的函数内联。 (可能,但肯定无法保证)
  • 参考vs值的选择取决于内联函数。 (完全相同的性能考虑将适用于非内联函数)
  • 它有所作为,你可以通过这样的微不足道的改变来超越编译器(编译器将在任何一种情况下应用相同的优化)
  • 并且优化实际上会在性能方面产生可衡量的差异。 (即使没有,差异也会小到可以忽略不计。)
  

我知道整数类型应该是   通过价值传递。但是,我   担心编译器可能会   内联ProcessByValue包含一个   复制。对此有规定吗?

是的,它会创建一个副本。就像通过引用传递将创建引用。然后,至少对于像int这样的简单类型,编译器会再次消除这两种类型。 内联函数不允许更改函数的行为。如果你创建函数来获取一个值参数,它的行为就像给它一个值参数一样,无论它是否内联。如果您定义要引用的函数,它将表现为传递引用,无论它是否内联。这样做是什么导致了正确的行为。

答案 1 :(得分:19)

应根据对函数有意义的内容输入参数。

如果函数采用基本类型,则按值传递是有意义的。我认识的一些人会抱怨如果它被const ref传递(因为它是“不必要的”),但我不认为我会抱怨。如果函数采用用户定义的类型并且不修改参数,那么通过const ref是有意义的。

如果它是用户定义的类型并且参数被修改,那么函数的语义将指示它应该如何传递。

答案 2 :(得分:7)

编译器应该能够优化内联函数,以便任一方法都能生成相同的代码。做一个最清楚的。

如果有疑问,请尝试一下。打开编译器的汇编列表输出,看看是否存在差异。

答案 3 :(得分:2)

如果类型小于或等于指针,则按值传递;例如,int,char,double,small structs,...

通过引用传递更大的对象;例如,STL容器。我已经阅读了很多关于编译器能够对其进行优化的内容,但他们并没有遵循我的简单基准。除非您想浪费时间测试用例,否则请使用const T& obj

奖励:为了更快速地使用来自c99的restrict(这样你就赶上了fortran,它限制了指针别名;用例:f(const T&__restrict__ obj)。C ++标准不允许{{1关键字但编译器使用内部关键字 - g ++使用restrict。如果代码中没有别名,则没有速度增益。

基准与g ++ 4.9.2:

通过引用传递向量:

__restrict__

按值传递矢量需要两倍的时间:

> cat inpoint.cpp
#include <vector>
#include <iostream>

using namespace std;

inline int show_size(const vector<int> &v) {
  return v.size();
}

int main(){
  vector<int> v(100000000);
  cout << show_size(v) << endl;
  return 0;
}
> g++ -std=c++14 -O2 inpoint.cpp; time ./a.out
100000000

real    0m0.330s
user    0m0.072s
sys     0m0.256s

答案 4 :(得分:0)

解决这个问题的最佳方法是创建一个同时执行这两个操作的测试平台,构建优化版本的代码,并将其检出程序集。您将立即看到特定编译器和特定用例的内容。

当它真正归结为它时,做你认为你的类的用户期望从界面。当您拥有所有构建和工作时,测量并找出您的瓶颈所在。很可能,这可能造成的任何差异(并且它不可能产生任何差异)将被代码中其他地方的更大性能问题所淹没。

答案 5 :(得分:0)

如果您的编译器不够智能,无法优化掉未修改的本地副本,那么优化掉本地引用可能不够智能。在这种情况下,它将为传递引用的情况生成更可怕的代码(导致每次访问都是间接的)。

答案 6 :(得分:0)

一个非常简短的答案:在决定是通过引用传递还是通过值处理内联函数和非内联函数时相同。

答案 7 :(得分:0)

在原语的情况下,它并不重要,因为你只传递了4个字节。

传递引用的原因是因为它的大小为4个字节,并且在自定义类型和大字符串的情况下,这是一个大幅减小的大小。

争论的焦点是......通常。

对于内联函数,您希望所有非基元的类型都可以通过引用传递,因为您告诉编译器首先将其内联。

答案 8 :(得分:-1)

一般来说,

仅将输出原语声明为引用。

如果需要禁止表达式,则仅将输入原语声明为引用或const引用:

int two = plus1( 1 );  //  compile error if plus1 is declared as "int plus1( int& )"

double y = sqrt( 1.1 * 2 );  // compile error if sqrt is declared as "double sqrt( const double& )"