与通过值传递和通过const ref传递的程序集不同

时间:2018-10-11 07:58:30

标签: c++ gcc clang

假设我们有这样的代码:

template<typename T>
struct StrongValue{
    constexpr const T &operator()() const {
        return value;
    }

    T &operator()(){
        return value;
    }

    constexpr const T &get() const {
        return value;
    }

    T &get(){
        return value;
    }

    T value;
};

using myint = int; // try double too

using m = StrongValue<myint>;

myint sum2(const m &a, const m &b){
    return a() + b();
}

myint sum2a(const m a, const m b){
    return a() + b();
}

myint sum1(myint a, myint b){
    return a + b;
}

int main(){
    constexpr m a{5};
    constexpr m b{5};

    return sum2a(a, b);
}

在clang和gcc中,-O3程序集如下所示:

sum2(StrongValue<int> const&, StrongValue<int> const&):
  mov eax, DWORD PTR [rsi]
  add eax, DWORD PTR [rdi]
  ret
sum2a(StrongValue<int>, StrongValue<int>):
  lea eax, [rdi+rsi]
  ret
sum1(int, int):
  lea eax, [rdi+rsi]
  ret
main:
  mov eax, 10
  ret

为什么sum2这样编译?

这是因为编译器会在省略引用的情况下更改函数签名,因此不允许这样做。

这是否意味着sum2如果没有内联,则比sum2a更昂贵?

2 个答案:

答案 0 :(得分:1)

  

为什么sum2会这样编译?

这是预期的。对于sum2,您传递了两个参考。 Reference和UIApplication是C ++的东西,CPU没有它们,对于CPU来说它们只是指针。因此,在该函数中,代码必须从内存中获取两个值并将它们求和。

另两个版本按值接收参数。

对于所有3个版本,编译器都选择使用UIApplication调用约定,这就是为什么在const__regcall寄存器中传递两个参数的原因。这就是其他两个版本仅用一条指令即可计算结果的方式。

  

这是否意味着,如果未内联,sum2比sum2a昂贵吗?

通常,是的。您不应通过const引用传递整数,而应通过值传递整数。但是,确切的性能影响可以忽略不计,唯一的发现方法是分析。

答案 1 :(得分:0)

不同之处在于,sum2的参数本质上是指针,而sum2a的参数是值。这意味着必须在sum2中取消对指针的引用才能获取可以添加的实际值,而在sum2a中可以立即添加值。

过度使用常量引用是一个常见错误。对于廉价复制的对象,通常最好按值传递参数。