C++ 与传递值和传递常数ref的程序集不同
假设我们有这样的代码:C++ 与传递值和传递常数ref的程序集不同,c++,gcc,clang,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 valu
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);
}
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
模板
结构strong值{
constexpr const T&运算符()()const{
返回值;
}
T运算符()(){
返回值;
}
constexpr const T&get()const{
返回值;
}
获取(&G)(){
返回值;
}
T值;
};
使用myint=int;//再试试双倍
使用m=strong值;
myint sum2(持续并购、持续并购){
返回a()+b();
}
myint sum2a(常数m a,常数m b){
返回a()+b();
}
myint sum1(myint a、myint b){
返回a+b;
}
int main(){
constexpr m{5};
constexpr mb{5};
返回sum2a(a,b);
}
在clang和gcc中,-O3汇编如下所示:
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);
}
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(StrongValue const&,StrongValue const&):
mov eax,DWORD PTR[rsi]
添加eax、DWORD PTR[rdi]
ret
sum2a(strong值,strong值):
lea eax,[rdi+rsi]
ret
sum1(整数,整数):
lea eax,[rdi+rsi]
ret
主要内容:
mov-eax,10
ret
为什么sum2
是这样编译的
这是因为编译器将更改函数签名,如果它忽略引用,则不允许这样做
这是否意味着,如果不是内联的,
sum2
比sum2a
更昂贵?区别在于sum2
的参数本质上是指针,而sum2a
的参数是值。这意味着在sum2
中,必须取消对指针的引用,以获得可以添加的实际值,而在sum2a
中,可以立即添加值
过度使用常量引用是一个常见的错误。对于具有廉价复制的对象,通常最好按值传递参数
为什么sum2是这样编译的
这是意料之中的。对于sum2,您传递了两个引用。引用和\uu regcall
调用约定,这就是为什么这两个参数在RSI
和RDI
寄存器中传递的原因。这就是其他两个版本如何在一条指令中计算结果
这是否意味着,如果不是内联的,sum2比sum2a更昂贵
一般来说,是的。您不应该通过常量引用传递整数,而是通过值传递它们。但是,确切的性能影响可以忽略不计,唯一的方法是分析。但如果没有内联/静态/匿名命名空间,编译器不允许重写
sum2
,并且必须使用指针?如果是在外部文件中,函数将永远不会内联?编译器通常尊重函数原型。现代编译器有各种各样的功能,在这种情况下有帮助,VC++有全局优化,也称为链接时间代码生成,LLVM有链接时间优化。它们甚至可能跨文件内联这些简单函数,从而消除RAM引用。但为了获得最佳性能,更可靠的方法是以一种有助于编译器以更少的技巧生成高效代码的方式编写代码。