2015-11-02 15 views
5

Załóżmy, że mam:Jaka jest różnica między auto a = A (3) i A a (3)?

struct A 
{ 
    A(int x) : m_x(x) { } 
    A(A&&) = delete; 
    int m_x; 
} 

i:

A a(3); // Ok 
auto a = A(3); // Error: function A(int&&) cannot be referenced - it's a deleted function 

Dlaczego ostatnie wywołanie konstruktora ruch? Dlaczego te dwa stwierdzenia różnią się pod względem generowanego kodu?

Odpowiedz

4

Dlaczego można oczekiwać, że obie ścieżki kodowe będą takie same?

Ty oczywiście konstruowanie anonimowego przedmiotu w drugim przykładzie, przeznaczając a (auto a), a następnie kopiując od tymczasowej do a (który to ruch, ponieważ mówimy o tymczasowym obiektem).

+0

Odnośnie pierwszego komentarza, mam wrażenie, że jest to często przedstawiane jako "są one takie same, z wyjątkiem kwestii technicznych, które zazwyczaj nie pojawiają się". Ale może wkładam słowa w usta OP. – Hurkyl

+1

Ktokolwiek wprowadza je jako taki, powinien zostać zwolniony z powodu niekompetencji. – Blindy

+0

Dlaczego kompilator generuje inny kod, gdy intencja jest wyraźnie taka sama? Czy istnieje przypadek, w którym programista będzie chciał tego zachowania? – Shmoopy

-1

Nie ma znaczenia, że ​​kompilator wygeneruje ten sam kod w obu przypadkach, ponieważ w czasie kompilacji potrzebne są zdefiniowane niezbędne funkcje/konstruktory. Pomyśl o tym w ten sposób - optymalizacja nastąpi po kompilacji/analizie kodu, ale w kodzie końcowym będzie (powinno być) takie samo w tym przypadku.

+0

kod zostanie skompilowany do tego samego kodu maszynowego * z optymalizacjami na * – CoffeeandCode

6

auto a = A(3); oznacza to samo, co A a = A(3);, ponieważ typem prawej strony jest A.

Oznacza to dokładnie jak to wygląda: A(3) tworzy tymczasowy A zainicjowany z 3, a następnie A a = _____ oznacza: stworzyć A nazywa a z _____ jako inicjatora.

Tworzysz tymczasowy plik, przekazujesz go jako a jako inicjator, a następnie tymczasowy plik zostaje zniszczony. Ten rodzaj inicjowania (z =) nazywa się inicjowanie kopii (chociaż nie mylić tego z "kopią", to tylko słowo).

Konstruktor jest wybrany do konstrukcji a, która akceptuje A. Musi to być konstruktor copy lub move. A ma konstruktor ruchu i konstruktor kopiowania. Ten ostatni jest generowany domyślnie i definiowany jako usunięty, ponieważ istnieje konstruktor ruchu deklarowany przez użytkownika.

Definicja jako usunięta nie wpływa jednak na rozdzielczość przeciążania; a konstruktor ruchu jest w tym przypadku preferowany względem konstruktora kopiowania.

Twój kod próbuje wywołać funkcję d, która jest źle sformułowana, stąd błąd.

Należy zauważyć, że jeśli konstruktor ruchu nie został usunięty, to zastosowanie miałaby kopia elision. Występuje w niektórych okolicznościach, gdy zmienna jest inicjowana z tymczasowego lub zmienna lokalna jest zwracana przez wartość. Zasadą jest, że kompilator może używać tej samej pamięci zarówno dla obiektu a, jak i obiektu tymczasowego, i ominąć wywołanie konstruktora kopiowania/przenoszenia.

Większość/wszystkie kompilatory robią to w takiej sytuacji. Możesz więc napisać auto a = A(3); iw praktyce nie dostaniesz niepotrzebnych ruchów. Jeśli napiszesz kod dla twojego konstruktora ruchu, który coś wypisze, będziesz miał nadzieję, że nic nie jest wyprowadzane.

Jeśli chcesz mieć absolutną pewność, że nie ma niepotrzebnej kopii lub skonstruować obiektu, który nie ma użytecznych kopii ani przenieść konstruktora - przestań pisać kod, który określa niepotrzebne kopie! A a(3); jest wystarczająca.

+0

Zastanawiam się, czy kompilator może przenieść ruch w tym przypadku, gdzie konstruktor ruchu zostanie usunięty? – Mikhail

+0

@Mikhail przepraszam, błędne przeczytanie pytania. zaktualizuje moją odpowiedź –

Powiązane problemy