Имеют ли ссылки на rvalue те же издержки, что и ссылки на lvalue?

Рассмотрим этот пример:

#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.

2 ответа

Решение

В вашей конкретной ситуации, скорее всего, они все одинаковы. В результате код от 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)

Эффективность передачи параметров зависит от ABI.

Например, в Linux Itanium C++ ABI указывает, что ссылки передаются в виде указателей на указанный объект:

3.1.2 Контрольные параметры

Опорные параметры обрабатываются путем передачи указателя на фактический параметр.

Это не зависит от ссылочной категории (rvalue/lvalue reference).

Для более широкого представления я нашел эту цитату в документе Технического университета Дании, в котором содержится соглашение о вызовах, в котором анализируется большинство компиляторов:

Ссылки рассматриваются как идентичные указателям во всех отношениях.

Таким образом, rvalue и lvalue ссылаются на указатель на все ABI.

Другие вопросы по тегам