2015-08-12 17 views
15

Oto przykład kodu z C++ quizu:Dlaczego wywoływany jest konstruktor ruchu?

#include <iostream> 
struct X { 
    X(const char *) { std::cout << 1; } 
    X(const X &) { std::cout << 2; } 
    X(X &&) { std::cout << 3; } 
}; 

X f(X a) { 
    return a; 
} 

X g(const char * b) { 
    X c(b); 
    return c; 
} 

int main() { 
    f("hello"); 
    g("hello"); 
} 

Jaki będzie wyjście z programu?

myślę w ten sposób:

  1. f(X a) nazywa, a konstruktor niejawnie konwertuje const char* X, więc wyjście jest 1
  2. Ponieważ nie mamy obiekt do przechowywania powrót wartość, wartość powrotna jest odrzucane ma wyjścia
  3. g(const char*) nazywa i X c(b)X(const char*)wyjście jest 1
  4. Wartością zwracaną jest jeszcze raz odrzucone - ma wyjścia

Więc odpowiedź brzmi 11. Odpowiedź który został podany do quizu jest 131. odpowiedź, która mi się z g ++ 4.4. 4-13 jest 121.

mówi się, że ten kod został skompilowany z tym poleceniem:

g++ -std=c++11 -Wall -Wextra -O -pthread 

Skąd numer środkowy pochodzi? I dlaczego może być 3 lub 2?

+0

Kopiowanie elision (http://en.cppreference.com/w/cpp/language/copy_elision) nie jest obowiązkowe. Nie możesz polegać na tej optymalizacji. Tak więc w instrukcjach return nic nie można polegać. – Lingxi

+5

Kto napisał to jako "quiz"? Ten program może poprawnie wydrukować cztery różne wyjścia. –

+0

@ T.C. Jest to coś, co nazywa się quiz pubem C++ na ACCU 2014 rok – DoctorMoisha

Odpowiedz

17

Teoretycznie to może wydrukować dowolny z 131, 13313, 1313 i 1331. To dość głupie jak pytanie quizowe.

  • f("hello");:

    • "Witam" jest zamienione na tymczasową X przez konstruktora konwersji wydruki 1.
    • Tymczasowy X służy do inicjalizacji argumentu funkcji, wywołuje konstruktor ruchu, drukuje 3. To może zostać usunięte.
    • służy do inicjalizacji tymczasowej wartości zwracanej, wywołuje konstruktor ruchu, drukuje 3.Jest to parametr funkcji, więc żadna elizacja nie jest dozwolona, ​​ale powrót jest niejawnym ruchem.
  • g("hello");

    • "Witam" są używane do konstruowania c poprzez konstruktor konwersji, wydruki 1.
    • c służy do zainicjowania tymczasowej wartości zwracanej, wywołuje konstruktor ruchu, drukuje 3. To może zostać usunięte.

Pamiętaj, że funkcje zawsze trzeba budować rzeczy wracają, nawet jeśli to tylko odrzucane przez kod wywołującego.

Jeśli chodzi o drukowanie 2, dzieje się tak dlatego, że używany starożytny kompilator nie implementuje reguły typu implicit-move-when-return-a-local-variable.

+2

Czy istnieje powód, dla którego tymczasowy musi być użyty (i ewentualnie elided) do zainicjowania parametru 'f'? Dlaczego nie można zainicjować parametru bezpośrednio z argumentu? – Angew

+1

@Angew Argument passing to inicjowanie kopii. (Wstaw długi cytat z [dcl.init] /17.6.2 tutaj) –

+1

Dzięki, [dcl.init]/15 jest tym, czego mi brakowało. – Angew

3

Copy elision dotyczy oświadczenia return w g i ewentualnie w innym miejscu. Cytat z cppreference:

Kopiowanie elizja jest dozwolone tylko formą optymalizacji, które mogą zmienić się zaobserwować efekty uboczne. Ponieważ niektóre kompilatory nie wykonują operacji kopiowania w każdej sytuacji, w której jest to dozwolone (np. W trybie debugowania ), programy, które polegają na efektach ubocznych konstruktorów i destruktorów kopiowania/przenoszenia, nie są przenośne.

Tak więc, zgodnie z przykładowym kodem, dane wyjściowe nie mogą być wiarygodnie prognozowane w różnych implementacjach.

+0

Skopiowanie elizji nie ma zastosowania do zwrotu w 'f'. –

+0

@ T.C. Ups, to jest parametr. – Lingxi

Powiązane problemy