Co to jest copy elision I return value optimization?

Co to jest copy elision? Co to jest (nazwana) optymalizacja wartości zwrotnej? Co to oznacza?

W jakich sytuacjach mogą wystąpić? Czym są ograniczenia?

Author: Community, 2012-10-18

4 answers

Wprowadzenie

Przegląd techniczny- przejdź do tej odpowiedzi .

W przypadku typowych przypadków, w których występuje kopiowanie - przejdź do tej odpowiedzi .

Copy elision jest optymalizacją zaimplementowaną przez większość kompilatorów, aby zapobiec dodatkowym (potencjalnie kosztownym) kopiom w pewnych sytuacjach. To sprawia, że zwracanie przez wartość lub pass-by-wartość jest możliwe w praktyce (obowiązują ograniczenia).

To jedyna forma optymalizacji, która mija (ha!) zasada as-if - copy elision można zastosować nawet jeśli kopiowanie / przenoszenie obiektu ma efekty uboczne .

Poniższy przykład zaczerpnięty z Wikipedii :

struct C {
  C() {}
  C(const C&) { std::cout << "A copy was made.\n"; }
};

C f() {
  return C();
}

int main() {
  std::cout << "Hello World!\n";
  C obj = f();
}

W zależności od ustawień kompilatora, wszystkie następujące wyjścia są poprawne :

Hello World!
Wykonano kopię.
Wykonano kopię.

Hello World!
Wykonano kopię.

Witam Świat!

Oznacza to również, że można utworzyć mniej obiektów, więc nie można również polegać na określonej liczbie wywoływanych destruktorów. Nie powinieneś mieć krytycznej logiki wewnątrz konstruktorów Kopiuj / Przenieś lub destruktorów, ponieważ nie możesz polegać na ich wywołaniu.

Jeśli wywołanie konstruktora copy lub move zostanie przerwane, konstruktor ten musi nadal istnieć i musi być dostępny. Zapewnia to, że copy elision nie zezwala na kopiowanie obiektów, które nie są normalnie kopiowalne, np. dlatego, że mieć prywatny lub usunięty konstruktor Kopiuj/przenieś.

C++17: od C++17, Kopia Elision jest gwarantowana, gdy obiekt jest zwracany bezpośrednio:

struct C {
  C() {}
  C(const C&) { std::cout << "A copy was made.\n"; }
};

C f() {
  return C(); //Definitely performs copy elision
}
C g() {
    C c;
    return c; //Maybe performs copy elision
}

int main() {
  std::cout << "Hello World!\n";
  C obj = f(); //Copy constructor isn't called
}
 175
Author: Luchian Grigore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-31 03:25:35

Odniesienie standardowe

Dla mniej technicznego widoku & wprowadzenie - przejdź do tej odpowiedzi.

W przypadku typowych przypadków, w których występuje kopiowanie - przejdź do tej odpowiedzi.

Kopia elision jest zdefiniowana w standardzie w:

12.8 kopiowanie i przenoszenie obiektów klasy [class.Kopia]

Jako

31) gdy spełnione są pewne kryteria, implementacja może pominąć konstrukcję copy / move klasy obiekt, nawet jeśli konstruktor Kopiuj / przenieś i / lub Destruktor obiektu mają skutki uboczne. W takich przypadkach, implementacja traktuje źródło i cel pominiętej operacji kopiowania/przenoszenia jako po prostu dwa różne sposoby odwoływania się do tego samego obiektu, a zniszczenie tego obiektu następuje w późniejszym czasie kiedy oba obiekty zostałyby zniszczone bez optymalizacji.123 to odejście od copy/move operacje, zwane kopiowaniem , są dozwolone w następujące okoliczności (które mogą być łączone z wyeliminowanie wielu kopii):

- w instrukcji return w funkcji o typie Return klasy, gdy wyrażenie jest nazwą nieulotny obiekt automatyczny (inny niż funkcja lub parametr catch-clause) o tym samym cvunqualified Typ jako typ powrotu funkcji, operację kopiowania/przenoszenia można pominąć konstruując obiekt automatyczny bezpośrednio do wartości zwracanej przez funkcję

- w throw-wyrażenie, gdy operand jest nazwą nieulotnego obiektu automatycznego (innego niż funkcja lub parametr catch-clause), którego zakres nie rozciąga się poza koniec najskrytszego załączając try-block (jeśli taki istnieje), operacja copy/move Z operandu do wyjątku obiekt (15.1) można pominąć konstruując obiekt automatic bezpośrednio do obiektu exception

- gdy obiekt klasy tymczasowej, który nie został powiązany z referencją (12.2), być kopiowane/przenoszone do obiektu klasy o tym samym typie CV-unqualified, operacja kopiowania/przenoszenia może być pominięta przez budowa obiektu tymczasowego bezpośrednio do celu pominiętej kopii / ruchu

- gdy wyjątek-deklaracja obsługi wyjątków (Klauzula 15) deklaruje obiekt tego samego typu (z wyjątkiem CV-qualification) jako obiekt wyjątku (15.1) można pominąć operację kopiowania/przenoszenia traktując deklarację wyjątku jako alias wyjątku obiekt, Jeśli znaczenie programu pozostanie bez zmian z wyjątkiem wykonania konstruktorów i destruktorów dla obiektu zadeklarowanego przez wyjątek-deklaracja.

123) ponieważ tylko jeden obiekt jest niszczony zamiast dwóch, a jeden konstruktor kopiowania / przenoszenia nie jest wykonywany, nadal istnieje jeden obiekt niszczony dla każdego zbudowanego.

Podany przykład to:

class Thing {
public:
  Thing();
  ~Thing();
  Thing(const Thing&);
};
Thing f() {
  Thing t;
  return t;
}
Thing t2 = f();

I wyjaśnione:

Tutaj kryteria wyboru może być łączone w celu wyeliminowania dwóch wywołań do konstruktora kopiującego klasy Thing: skopiowanie lokalnego automatycznego obiektu t do tymczasowego obiektu dla zwracanej wartości funkcji f() i kopiowanie tego tymczasowego obiektu do obiektu t2. Skutecznie, budowa obiektu lokalnego t może być postrzegana jako bezpośrednia inicjalizacja obiektu globalnego t2, A zniszczenie tego obiektu nastąpi w programie wyjście. Dodanie konstruktora move Do Rzeczy ma taki sam efekt, ale jest to konstrukcja ruchu z obiekt tymczasowy t2, który jest elided.

 78
Author: Luchian Grigore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:03:07

Popularne formy kopiowania

Przegląd techniczny- przejdź do tej odpowiedzi .

Dla mniej technicznego widoku & wprowadzenie- przejdź do tej odpowiedzi .

(Named)Optymalizacja wartości zwrotnej jest powszechną formą kopiowania. Odnosi się do sytuacji, w której obiekt zwrócony przez wartość z metody ma swoją kopię. Przykład przedstawiony w standardzie ilustruje o nazwie return value optimization, ponieważ obiekt jest nazwany.

class Thing {
public:
  Thing();
  ~Thing();
  Thing(const Thing&);
};
Thing f() {
  Thing t;
  return t;
}
Thing t2 = f();

Regular return value optimization występuje, gdy zwracana jest wartość tymczasowa:

class Thing {
public:
  Thing();
  ~Thing();
  Thing(const Thing&);
};
Thing f() {
  return Thing();
}
Thing t2 = f();

Inne popularne miejsca, w których odbywa się kopiowanie, to gdy tymczasowy jest przekazywany przez wartość :

class Thing {
public:
  Thing();
  ~Thing();
  Thing(const Thing&);
};
void foo(Thing t);

foo(Thing());

Lub gdy wyjątek jest wyrzucany i przechwytywany przez wartość :

struct Thing{
  Thing();
  Thing(const Thing&);
};

void foo() {
  Thing c;
  throw c;
}

int main() {
  try {
    foo();
  }
  catch(Thing c) {  
  }             
}

Typowe ograniczenia kopiowania to:

  • wiele punktów zwrotnych
  • inicjalizacja warunkowa

Najbardziej komercyjna Kompilatory obsługują copy elision & (N) RVO (w zależności od ustawień optymalizacji).

 70
Author: Luchian Grigore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:37

Copy elision jest techniką optymalizacji kompilatora, która eliminuje niepotrzebne kopiowanie / przenoszenie obiektów.

W następujących okolicznościach kompilator może pominąć operacje kopiowania/przenoszenia, a tym samym nie wywoływać skojarzonego konstruktora:

  1. NRVO (Named Return Value Optimization) : Jeśli funkcja zwraca typ klasy według wartości, a wyrażenie instrukcji return jest nazwą nieulotnego obiektu z automatycznym czasem przechowywania (który nie jest parametr funkcji), wtedy kopiowanie/przenoszenie, które byłyby wykonywane przez nie optymalizujący kompilator, może zostać pominięte. Jeśli tak, zwracana wartość jest konstruowana bezpośrednio w magazynie, do którego zwracana wartość funkcji byłaby przenoszona lub kopiowana.
  2. RVO (Return Value Optimization) : Jeśli funkcja zwraca bezimienny obiekt tymczasowy, który byłby przenoszony lub kopiowany do miejsca docelowego przez naiwny kompilator, kopiowanie lub przenoszenie można pominąć zgodnie z 1.
#include <iostream>  
using namespace std;

class ABC  
{  
public:   
    const char *a;  
    ABC()  
     { cout<<"Constructor"<<endl; }  
    ABC(const char *ptr)  
     { cout<<"Constructor"<<endl; }  
    ABC(ABC  &obj)  
     { cout<<"copy constructor"<<endl;}  
    ABC(ABC&& obj)  
    { cout<<"Move constructor"<<endl; }  
    ~ABC()  
    { cout<<"Destructor"<<endl; }  
};

ABC fun123()  
{ ABC obj; return obj; }  

ABC xyz123()  
{  return ABC(); }  

int main()  
{  
    ABC abc;  
    ABC obj1(fun123());//NRVO  
    ABC obj2(xyz123());//NRVO  
    ABC xyz = "Stack Overflow";//RVO  
    return 0;  
}

**Output without -fno-elide-constructors**  
root@ajay-PC:/home/ajay/c++# ./a.out   
Constructor    
Constructor  
Constructor  
Constructor  
Destructor  
Destructor  
Destructor  
Destructor  

**Output with -fno-elide-constructors**  
root@ajay-PC:/home/ajay/c++# g++ -std=c++11 copy_elision.cpp -fno-elide-constructors    
root@ajay-PC:/home/ajay/c++# ./a.out   
Constructor  
Constructor  
Move constructor  
Destructor  
Move constructor  
Destructor  
Constructor  
Move constructor  
Destructor  
Move constructor  
Destructor  
Constructor  
Move constructor  
Destructor  
Destructor  
Destructor  
Destructor  
Destructor  

Nawet gdy kopiowanie ma miejsce i konstruktor copy-/move-nie jest wywoływany, musi być obecny i dostępny (tak jakby w ogóle nie doszło do optymalizacji), w przeciwnym razie program jest źle uformowany.

Powinieneś zezwalać na takie kopiowanie tylko tam, gdzie nie wpłynie to na obserwowalne zachowanie Twojego oprogramowania. Copy elision jest jedyną formą optymalizacji dozwoloną do uzyskania (tj. elide) obserwowalnych efektów ubocznych. Przykład:

#include <iostream>     
int n = 0;    
class ABC     
{  public:  
 ABC(int) {}    
 ABC(const ABC& a) { ++n; } // the copy constructor has a visible side effect    
};                     // it modifies an object with static storage duration    

int main()   
{  
  ABC c1(21); // direct-initialization, calls C::C(42)  
  ABC c2 = ABC(21); // copy-initialization, calls C::C( C(42) )  

  std::cout << n << std::endl; // prints 0 if the copy was elided, 1 otherwise
  return 0;  
}

Output without -fno-elide-constructors  
root@ajay-PC:/home/ayadav# g++ -std=c++11 copy_elision.cpp  
root@ajay-PC:/home/ayadav# ./a.out   
0

Output with -fno-elide-constructors  
root@ajay-PC:/home/ayadav# g++ -std=c++11 copy_elision.cpp -fno-elide-constructors  
root@ajay-PC:/home/ayadav# ./a.out   
1

GCC dostarcza -fno-elide-constructors opcja wyłączająca kopiowanie elision. Jeśli chcesz uniknąć możliwego usunięcia kopii, użyj -fno-elide-constructors.

Teraz prawie wszystkie Kompilatory zapewniają kopiowanie, gdy optymalizacja jest włączona (i jeśli żadna inna opcja nie jest ustawiona, aby ją wyłączyć).

Podsumowanie

Przy każdym usunięciu kopii pomija się jedną konstrukcję i jedno dopasowane zniszczenie kopii, co oszczędza czas procesora, a jeden obiekt nie jest tworzony, co oszczędza miejsce na ramce stosu.

 37
Author: Ajay yadav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-27 20:37:11