Рассмотрим этот пример:
#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
(и делает Foo noinline
) Я смог получить вывод, который выглядит как решение xaxxon.
В вашей конкретной ситуации, скорее всего, они все одинаковы. Полученный код от Godbolt с gcc -O3 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 Test)
Эффективность передачи параметров зависит от ABI.
Например, на Linux Itanium C ++ ABI указывает, что ссылки передаются как указатели на указанный объект:
3.1.2 Контрольные параметры
Опорные параметры обрабатываются путем передачи указателя на фактический параметр.
Это не зависит от ссылочной категории (rvalue / lvalue reference).
Для более широкого взгляда я нашел эту цитату в документе из Технического университета Дании, соглашение о вызовах, который анализирует большинство компиляторов:
Ссылки рассматриваются как идентичные указателям во всех отношениях.
Таким образом, rvalue и lvalue ссылаются на указатель на все ABI.