предположим, что SomeDataStruct «огромен», создаст ли современный компилятор эквивалентный и, следовательно, столь же эффективный код в следующих двух случаях?
Как вы определили объект, это зависит от компилятора , что происходит с объектом в функции. Давайте узнаем, что делает g cc на x86-64 Linux. Взгляните на следующий код:
struct big_
{
unsigned long w,x,y,z;
int pos;
};
unsigned long byval(const big_ big)
{
auto y = big.z;
y += big.y;
y += big.x;
y += big.w;
return y;
}
unsigned long byref(const big_& big)
{
auto y = big.z;
y += big.y;
y += big.x;
y += big.w;
return y;
}
Компиляция с помощью g++ -std=c++17 -O3
дает мне следующую сборку Godbolt link :
byval(big_):
mov rax, QWORD PTR [rsp+24]
add rax, QWORD PTR [rsp+32]
add rax, QWORD PTR [rsp+16]
add rax, QWORD PTR [rsp+8]
ret
byref(big_ const&):
mov rax, QWORD PTR [rdi+16]
add rax, QWORD PTR [rdi+24]
add rax, QWORD PTR [rdi+8]
add rax, QWORD PTR [rdi]
ret
Что мы можем увидеть в приведенном выше коде?
Аргументы первой функции byval
передаются в стек. Аргументы второй функции передаются через регистр rdi
(см. Соглашения о вызовах для вашей ОС, чтобы узнать, почему). Передача чего-либо через регистр всегда быстрее, чем передача в стек, потому что регистры ближе к cpu
, а стек находится где-то в кэше или оперативной памяти. Следовательно, здесь лучше передавать по ссылке. Если у вас есть небольшой объект (8 байт), лучше передать его по значению, потому что он все равно будет передан через регистр. Передача по стеку происходит только тогда, когда ваш объект настолько велик, что не может поместиться в регистр.
Еще мы видим, что byval
имеет параметр, отличный от const
. Компилятор просто сбросил его.
, поскольку я здесь использую «const» в обеих функциях;
Это не имеет значения, как вы можете видеть из объяснений выше.
void fooByValue(SomeDataStruct data);
изменить: предположим, что тип SomeDataStruct не имеет конструктора копирования.
Если вы не писали конструктор копирования, это не означает, что компилятор может не генерировать его неявно. Но предположим, вы его удалили. Так что теперь вы не можете его скопировать, и если вы попытаетесь, ваш код не скомпилируется. Однако, если вы определили конструктор перемещения, вы можете переместить его.
void fn()
{
fooByValue( std::move(data) ); // no copy
}
Использование const
не повлияет на производительность вашего кода. const
похож на контракт между вами и компилятором. Если вы отметите что-то как const
, компилятор не позволит вам это изменить. Если вы как-то его измените, это приведет к неопределенному поведению. Я предлагаю вам go и прочитать эту статью Артура О'Дуайера об этом топе c const - это контракт.