右值引用是否具有与右值引用相同的开销?



考虑这个例子:

#include <utility>
// runtime dominated by argument passing
template <class T>
void foo(T t) {}
int main() {
int i(0);
foo<int>(i); // fast -- int is scalar type
foo<int&>(i); // slow -- lvalue reference overhead
foo<int&&>(std::move(i)); // ???
}

foo<int&&>(i)是和foo<int>(i)一样快,还是像foo<int&>(i)一样涉及指针开销?

编辑:按照建议,运行g++ -S给了我相同的 51 行汇编文件,用于foo<int>(i)foo<int&>(i),但foo<int&&>(std::move(i))产生了 71 行汇编代码(看起来差异来自std::move(。

编辑:感谢那些推荐具有不同优化级别的g++ -S的人 - 使用-O3(并制作foonoinline(,我能够获得看起来像xaxxon解决方案的输出。

在您的具体情况中,它们很可能都是一样的。 使用 gcc -O3 的 godbolt 生成的代码 https://godbolt.org/g/XQJ3Z4 用于:

#include <utility>
// runtime dominated by argument passing
template <class T>
int foo(T t) { return t;}
int main() {
int i{0};
volatile int j;
j = foo<int>(i); // fast -- int is scalar type
j = foo<int&>(i); // slow -- lvalue reference overhead
j = foo<int&&>(std::move(i)); // ???
}

是:

mov     dword ptr [rsp - 4], 0 // foo<int>(i);
mov     dword ptr [rsp - 4], 0 // foo<int&>(i);
mov     dword ptr [rsp - 4], 0 // foo<int&&>(std::move(i)); 
xor     eax, eax
ret

volatile int j是编译器无法优化所有代码,否则它会知道调用的结果将被丢弃,整个程序将优化为零。

但是,如果您强制不内联该函数,那么事情会发生变化int __attribute__ ((noinline)) foo(T t) { return t;}

int foo<int>(int):                           # @int foo<int>(int)
mov     eax, edi
ret
int foo<int&>(int&):                          # @int foo<int&>(int&)
mov     eax, dword ptr [rdi]
ret
int foo<int&&>(int&&):                          # @int foo<int&&>(int&&)
mov     eax, dword ptr [rdi]
ret

上图:https://godbolt.org/g/pbZ1BT

对于这样的问题,请学会爱 https://godbolt.org 和 https://quick-bench.com/(快速板凳要求您学习如何正确使用Google测试(

参数传递的效率取决于 ABI。

例如,在 Linux 上,Itanium C++ ABI 指定引用作为指向引用对象的指针传递:

3.1.2 参考参数

引用参数是通过传递指向实际参数的指针来处理的。

这与引用类别(右值/左值引用(无关。

为了更广泛地看待,我在丹麦技术大学的一份文档中找到了这句话,称为约定,它分析了大多数编译器:

引用在所有方面都被视为与指针相同。

因此,右值和左值引用涉及所有 ABI 上的指针开销。

相关内容

  • 没有找到相关文章

最新更新