C++, zwracanie obiektu

0
class K{
    public:
   K(int  a) : a(a){}
    int a;
    int b;
    int c;
    int d;
    
    K func(){
        a = 2;
        return K(2);
    }
};
int main(){
    K k(1);
k.func();
    return 0;
}

             	.Ltext0:
             		.section	.text._ZN1KC2Ei,"axG",@progbits,_ZN1KC5Ei,comdat
             		.align 2
             		.weak	_ZN1KC2Ei
             	_ZN1KC2Ei:

             	.LFB1:

             		.cfi_startproc
0000 55       		pushq	%rbp
             		.cfi_def_cfa_offset 16
             		.cfi_offset 6, -16
0001 4889E5   		movq	%rsp, %rbp
             		.cfi_def_cfa_register 6
0004 48897DF8 		movq	%rdi, -8(%rbp)
0008 8975F4   		movl	%esi, -12(%rbp)

             	.LBB2:

000b 488B45F8 		movq	-8(%rbp), %rax
000f 8B55F4   		movl	-12(%rbp), %edx
0012 8910     		movl	%edx, (%rax)

             	.LBE2:

0014 5D       		popq	%rbp
             		.cfi_def_cfa 7, 8
0015 C3       		ret
             		.cfi_endproc

             	.LFE1:

             		.weak	_ZN1KC1Ei
             		.set	_ZN1KC1Ei,_ZN1KC2Ei
             		.section	.text._ZN1K4funcEv,"axG",@progbits,_ZN1K4funcEv,comdat
             		.align 2
             		.weak	_ZN1K4funcEv
             	_ZN1K4funcEv:

             	.LFB3:

             		.cfi_startproc
0000 55       		pushq	%rbp
             		.cfi_def_cfa_offset 16
             		.cfi_offset 6, -16
0001 4889E5   		movq	%rsp, %rbp
             		.cfi_def_cfa_register 6
0004 4883EC20 		subq	$32, %rsp
0008 48897DE8 		movq	%rdi, -24(%rbp)

000c 488B45E8 		movq	-24(%rbp), %rax
0010 C7000200 		movl	$2, (%rax)
     0000

0016 488D45F0 		leaq	-16(%rbp), %rax
001a BE020000 		movl	$2, %esi
     00
001f 4889C7   		movq	%rax, %rdi
0022 E8000000 		call	_ZN1KC1Ei
     00
0027 488B45F0 		movq	-16(%rbp), %rax
002b 488B55F8 		movq	-8(%rbp), %rdx

002f C9       		leave
             		.cfi_def_cfa 7, 8
0030 C3       		ret
             		.cfi_endproc

             	.LFE3:

             		.text
             		.globl	main
             	main:

             	.LFB4:

             		.cfi_startproc
0000 55       		pushq	%rbp
             		.cfi_def_cfa_offset 16
             		.cfi_offset 6, -16
0001 4889E5   		movq	%rsp, %rbp
             		.cfi_def_cfa_register 6
0004 4883EC10 		subq	$16, %rsp

             	.LBB3:

0008 488D45F0 		leaq	-16(%rbp), %rax
000c BE010000 		movl	$1, %esi
     00
0011 4889C7   		movq	%rax, %rdi
0014 E8000000 		call	_ZN1KC1Ei
     00

0019 488D45F0 		leaq	-16(%rbp), %rax
001d 4889C7   		movq	%rax, %rdi
0020 E8000000 		call	_ZN1K4funcEv
     00

0025 B8000000 		movl	$0, %eax
     00

             	.LBE3:

002a C9       		leave
             		.cfi_def_cfa 7, 8
002b C3       		ret
             		.cfi_endproc

             	.LFE4:

             	.Letext0:

Rozważmy linię: 0004 4883EC20 subq $32, %rsp
Czy dobrze tłumaczę sobie, że jest to zaalokowanie pamięci na stosie dla zwracanego obiektu i po powrocie z funkcji rejestr %rax będzie zawierał adres tego obiektu na stosie?
W takim razie zasadniczo tutaj przy zwracanaiu nie odbywa się żadne kopiowanie. Jest po prostu "ulokowanie" obiektu w odpowiednim miejscu. Jak zatem ma się do tego krytykowanie zwracania przez wartość ( oczywiscie wiadomo, że są sytuacje, że trzeba zwracać przez wartość).

5

Obserwacje masz słuszne (patrz EDIT 4 niżej) - optymalizacja ta nowi nazwę RVO - Return Value Optimization - i standard C++ dopuszcza explicite jej stosowanie.
Krytykowanie zwracania przez wartość ma sens jedynie w niewielu przypadkach - Wiki podaje jeden z nich:

std::string f(bool cond = false) {
  std::string first("first");
  std::string second("second");
  // the function may return one of two named objects
  // depending on its argument. RVO might not be applied
  return cond ? first : second;
}

W innym przypadku krytykowanie jest nieuzasadnione, chyba, że wiadomo, że kompilator z którego korzysta się w danym projekcie nie robi RVO (to musiał by być bardzo dziwny kompilator).

EDIT: Warto też poczytać o copy elision (zresztą, wiki do tego linkuje przy okazji RVO). C++ nie jest łatwy ;)

EDIT 4: Dobra, przeanalizowałem na spokojnie kod który wkleiłeś - tam nie ma RVO :) (na początku wziąłem kod który robi a=2 za RVO, ale potem zauważyłem, że w kodzie w C++ masz a=2).
W przypadku Twojego kodu cały obiekt (zajmujący 16 bajtów) jest zwracany w rejestrach RAX i RDX - zauważ że na końcu func jest kopiowanie zaalokowanego na stosie obiektu do tych rejestrów:
0027 488B45F0 movq -16(%rbp), %rax
002b 488B55F8 movq -8(%rbp), %rdx

Z tego co poszperałem, to RVO w G++ jest włączane dopiero przy -O2, ew. z opcją -felide-constructors. Natomiast w tym wypadku kompilator nie zdecydował się na RVO z jakiegoś powodu przy użyciu tylko tej drugiej opcji, a próba wygenerowania powyższego przykładu z -O2 odpala deadcode elimination i zostaje tylko return 0 ;)

Niemniej jednak RVO istnieje (nie w tym przykładzie, ale ogólnie) i się je stosuje.

Ah, co do tej linii co wskazałeś - to alokacja obiektu na stosie w metodzie func, czyli w zasadzie dowód na to, że w tym wypadku nie ma RVO.

0

Jak zatem ma się do tego krytykowanie zwracania przez wartość

Nieaktualne. Ale jak zwracasz przez wartość, to najlepiej zwracaj jedną statyczną zmienną bezwarunkowo:

{
    Klasa result;
    ....
    return result;
}

unikaj czegoś takiego:

{
    Klasa vara, varb;
    ...
    if (cośtam)
        return vara;
    else
        return varb;
}
0

Nieaktualne. Ale jak zwracasz przez wartość, to najlepiej zwracaj jedną statyczną zmienną bezwarunkowo:

Chodzi o niestatyczną, zgadza się?

1 użytkowników online, w tym zalogowanych: 0, gości: 1