Je nějaký rozdíl v jazyce C ++ mezi inicializace kopírování a přímým inicializace?

hlasů
199

Předpokládejme, že mám tuto funkci:

void my_test()
{
    A a1 = A_factory_func();
    A a2(A_factory_func());

    double b1 = 0.5;
    double b2(0.5);

    A c1;
    A c2 = A();
    A c3(A());
}

V každé skupině jsou tyto výroky totožné? Nebo je tam navíc (případně optimalizovatelných) kopie v některých initializations?

Viděl jsem lidi říkají obě věci. Prosím, citovat text jako důkaz. Také přidat další případy prosím.

Položena 26/06/2009 v 22:36
zdroj uživatelem
V jiných jazycích...                            


9 odpovědí

hlasů
217

C ++ 17 aktualizace

V C ++ 17, význam A_factory_func()změnila z vytvoření dočasného objektu (C ++ <= 14), jen určující inicializaci jakýkoliv objekt, tento výraz je inicializován (volně mluvení) v C ++ 17. Tyto objekty jsou proměnné vytvořené prohlášením (podobně a1) nebo umělé objekty vytvořené při inicializaci končí právě vyřazené jinak nebo pokud je potřeba objekt za referenční vazebnou (podobně, A_factory_func();je uměle vytvořený objekt, s názvem „dočasná zhmotnění“ protože A_factory_func()nemá proměnnou nebo odkaz, který by jinak vyžadovaly objekt existovat). Také tyto předměty jsou nazývány „výsledkové objekty“.

Jako příklady v našem případě, v případě a1a a2zvláštní pravidla říkají, že v těchto prohlášeních, objekt výsledkem prvalue inicializátor stejného typu, jako a1je variabilní a1, a proto A_factory_func()přímo inicializuje objekt a1. Každý zprostředkovatel funkční stylu obsazení nebude mít žádný vliv, protože A_factory_func(another-prvalue)jen „projde“ objektu výsledků vnějšího prvalue být rovněž předmětem výsledkem vnitřní prvalue.


A a1 = A_factory_func();
A a2(A_factory_func());

Záleží na tom, jaký typ A_factory_func()vrací. Předpokládám, že vrací A- pak je to to samé - kromě toho, že při kopírovací konstruktor je výslovně, pak první, kdo selže. Číst 8,6 / 14

double b1 = 0.5;
double b2(0.5);

To je to samé, protože je to vestavěný typu (to znamená, že není typ třídy zde). Přečíst 8.6 / 14 .

A c1;
A c2 = A();
A c3(A());

To není to samé. První default-inicializuje, jestliže Aje non-POD a nedělá žádnou inicializace na POD (Read 8.6 / 9 ). Druhá kopie inicializuje: hodnota inicializuje dočasné a zkopíruje tato hodnota do c2(čtení 5.2.3 / 2 a 8,6 / 14 ). To samozřejmě bude vyžadovat non-explicitní kopie konstruktor (Read 8,6 / 14 a 12.3.1 / 3 a 13.3.1.3/1 ). Třetí vytváří deklarace funkce pro funkci c3, která vrátí Aa vezme ukazatel funkce k funkci vracející A(Read 8.2 ).


Ponoří do inicializace Direct a kopírování inicializace

Zatímco oni vypadají stejně a mají to samé, tyto dvě formy jsou pozoruhodně odlišné v určitých případech. Obě formy inicializace jsou přímé a kopírování inicializaci:

T t(x);
T t = x;

Tam je chování můžeme přiřadit ke každému z nich:

  • Přímý inicializace se chová jako volání funkce na přetížené funkce: funkce, v tomto případě, jsou stavitelé T(včetně explicitones), a argument x. Rozlišení přetížení najde nejlepší odpovídající konstruktor, a když bude dělat potřebné vyžadováno implicitní konverze.
  • Inicializace kopie konstrukce implicitní konverze sekvence: Snaží se převést xna objekt typu T. (To pak může kopírovat přes tento objekt do objektu k inicializaci, takže kopie konstruktor je potřeba taky - ale to není důležité níže)

Jak vidíte, inicializace kopie je nějakým způsobem součástí přímé inicializace s ohledem na možné implicitní konverze: Zatímco přímé inicializace má všechny konstruktérů k dispozici pro volání, a navíc mohou dělat žádnou implicitní převod, kterou potřebuje, aby se vyrovnal typů argumentů, copy inicializace stačí nastavit jednu implicitní konverze sekvence.

Snažil jsem se a dostal následující kód výstupu jiného textu pro každý z těchto forem , bez použití „jasné“ skrze explicitkonstruktérů.

#include <iostream>
struct B;
struct A { 
  operator B();
};

struct B { 
  B() { }
  B(A const&) { std::cout << "<direct> "; }
};

A::operator B() { std::cout << "<copy> "; return B(); }

int main() { 
  A a;
  B b1(a);  // 1)
  B b2 = a; // 2)
}
// output: <direct> <copy>

Jak to funguje a proč to dělá výstup, které jsou výsledkem?

  1. Přímý inicializace

    Nejprve se neví nic o konverzi vědět. Bude to jen zkusit zavolat konstruktor. V tomto případě následující konstruktor je k dispozici a je to přesná shoda :

    B(A const&)
    

    Neexistuje žádná konverze, natož uživatelem definované konverze, potřeboval zavolat, že konstruktor (všimněte si, že žádný const konverze kvalifikace se tu děje buď). A tak přímé inicializace bude říkat.

  2. inicializace kopírování

    Jak bylo uvedeno výše, bude inicializace kopie konstruktu sekvenci konverze, pokud anení zadat Bnebo z něj odvozený (což je zjevně v tomto případě). Tak to bude hledat způsoby, jak dělat svou konverzi, a najdete tyto kandidáty

    B(A const&)
    operator B(A&);
    

    Všimněte si, jak jsem přepsal konverzní funkce: Typ parametru odráží typ thisukazatel, který členské funkce non-const je non-const. Nyní, nazýváme tyto kandidáty xjako argument. Vítězem je konverze funkce: Vzhledem k tomu, když máme dvě kandidátní funkce jak přijímat odkaz na stejného typu, pak méně const verze vyhraje (to je mimochodem také mechanismus, který upřednostňuje funkci non-const člen vyzývá k non -const objekty).

    Všimněte si, že pokud se změní funkce převodu být const členské funkce, pak je konverze nejednoznačný (protože oba mají typ parametru A const&pak): The Comeau překladač jej správně odmítá, ale GCC jej přijímá v režimu bez pedantský. Přepnutí do -pedanticumožňuje výstup varování příliš správné dvojznačnost, ačkoli.

Doufám, že to pomůže trochu, aby bylo jasnější, jak se tyto dvě formy se liší!

Odpovězeno 26/06/2009 v 23:04
zdroj uživatelem

hlasů
38

Přiřazení se liší od inicializace .

Obě z následujících řádků provést inicializaci . Jediný konstruktor volání se provádí:

A a1 = A_factory_func();  // calls copy constructor
A a1(A_factory_func());   // calls copy constructor

ale není to ekvivalent:

A a1;                     // calls default constructor
a1 = A_factory_func();    // (assignment) calls operator =

Nemám text v okamžiku, kdy se dokázat to, ale je to velmi snadné, aby experiment:

#include <iostream>
using namespace std;

class A {
public:
    A() { 
        cout << "default constructor" << endl;
    }

    A(const A& x) { 
        cout << "copy constructor" << endl;
    }

    const A& operator = (const A& x) {
        cout << "operator =" << endl;
        return *this;
    }
};

int main() {
    A a;       // default constructor
    A b(a);    // copy constructor
    A c = a;   // copy constructor
    c = b;     // operator =
    return 0;
}
Odpovězeno 26/06/2009 v 22:42
zdroj uživatelem

hlasů
12

double b1 = 0.5; je implicitní volání konstruktoru.

double b2(0.5); je explicitní volání.

Podívejte se na následující kód vidět rozdíl:

#include <iostream>
class sss { 
public: 
  explicit sss( int ) 
  { 
    std::cout << "int" << std::endl;
  };
  sss( double ) 
  {
    std::cout << "double" << std::endl;
  };
};

int main() 
{ 
  sss ddd( 7 ); // calls int constructor 
  sss xxx = 7;  // calls double constructor 
  return 0;
}

Pokud je vaše třída nemá žádné explicitní constuctors než explicitních a implicitních hovory jsou identické.

Odpovězeno 26/06/2009 v 22:54
zdroj uživatelem

hlasů
4

Poznámky:

[12,2 / 1] Temporaries of class type are created in various contexts: ... and in some initializations (8.5).

Tedy pro kopírování inicializaci.

[12,8 / 15] When certain criteria are met, an implementation is allowed to omit the copy construction of a class object ...

Jinými slovy, bude to dobrý kompilátor není vytvořit kopii pro kopírování inicializace, kdy se lze vyhnout; místo toho to bude jen volat konstruktor přímo - tedy stejně jako pro přímé inicializace.

Jinými slovy, copy-inicializace je stejně jako přímé inicializace ve většině případů <názoru>, kde je pochopitelný kód byl napsán. Vzhledem k tomu, direct-inicializace potenciálně způsobí libovolné (a tudíž pravděpodobně neznámé) konverze, raději vždy používat copy-inicializace, když je to možné. (S bonusem, že to ve skutečnosti vypadá inicializace.) </ Stanovisko>

Technické goriness: [12,2 / 1 pokr shora] Even when the creation of the temporary object is avoided (12.8), all the semantic restrictions must be respected as if the temporary object was created.

Jsem rád, že nejsem psaní C ++ kompilátor.

Odpovězeno 07/10/2011 v 21:20
zdroj uživatelem

hlasů
3

Záznamník s ohledem na tuto část:

C2 = A (); C3 (A ());

Vzhledem k tomu, většina z odpovědí jsou pre-c ++ 11 přidávám co c ++ 11 má říci asi toto:

Jednoduchý typ-specifikátor (7.1.6.2), nebo typename-specifikátor (14.6) a následně v závorkách expresním-seznam konstruuje hodnotu určitého typu daného seznamu výraz. V případě, že seznam exprese je jediný výraz, výraz konverze typu je ekvivalentní (v definedness, a pokud je definován ve významu) na odpovídající odlitku výrazu (5.4). Je-li zadán typ je typ třídy, bude třída typ být kompletní. V případě, že seznam výraz specifikuje více než jednu hodnotu, pak se typ být třída s vhodně deklarované konstruktoru (8,5, 12,1), a exprese T (x1, X2, ...), je ekvivalentní jejich prohlášení T t (x1, x2, ...); pro některé vynalezl dočasné proměnné t, s tím výsledkem, že se hodnota t jako prvalue.

Takže optimalizace jsou nebo nejsou rovnocenné podle standardu. Všimněte si, že je to v souladu s tím, co ostatní odpovědi byly zmíněny. Jen cituje standard má říkat kvůli správnosti.

Odpovězeno 18/05/2015 v 04:26
zdroj uživatelem

hlasů
3

První skupina: záleží na tom, co A_factory_funcse vrátí. Na prvním řádku je příklad inicializace kopírování , druhý řádek je přímá inicializace . Pokud A_factory_funcse vrátí Aobjekt pak jsou rovnocenné, oba říkají kopírovací konstruktor A, jinak je první verze vytváří rvalue typu Az dostupných operátorů konverze pro návratový typ A_factory_funcnebo příslušných Akonstruktérů, a pak zavolá kopírovací konstruktor postavit a1z toho dočasný. Druhá verze se snaží najít vhodný konstruktor, který trvá bez ohledu A_factory_funcvrátí, nebo že něco bere, že návratová hodnota může být implicitně převeden na.

Druhá skupina: naprosto stejná logika platí, kromě toho, že postavený v typy nemají žádné exotické konstruktérů, takže jsou v praxi totožné.

Třetí skupina: c1je výchozí inicializaci c2je kopírování inicializuje z inicializuje dočasný hodnota. Jakékoliv členy c1, které mají pod-typu (nebo členové členů, atd, atd.) Nemusí být inicializován, pokud uživatel dodáván výchozí konstruktéři (pokud existují) nejsou explicitně inicializovat. Pro c2, záleží na tom, zda je uživatel dodávají kopii konstruktor a zda odpovídajícím způsobem inicializuje ty členy, ale dočasných vše bude inicializovat (zero-inicializaci Pokud není uvedeno jinak explicitně inicializovat). Jako litb všiml, c3je to past. Je to vlastně deklarace funkce.

Odpovězeno 26/06/2009 v 22:55
zdroj uživatelem

hlasů
1

Můžete vidět její rozdíl explicita implicittypů konstruktérů při inicializaci objektu:

Třídy :

class A
{
    A(int) { }      // converting constructor
    A(int, int) { } // converting constructor (C++11)
};

class B
{
    explicit B(int) { }
    explicit B(int, int) { }
};

A do main funkce:

int main()
{
    A a1 = 1;      // OK: copy-initialization selects A::A(int)
    A a2(2);       // OK: direct-initialization selects A::A(int)
    A a3 {4, 5};   // OK: direct-list-initialization selects A::A(int, int)
    A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int)
    A a5 = (A)1;   // OK: explicit cast performs static_cast

//  B b1 = 1;      // error: copy-initialization does not consider B::B(int)
    B b2(2);       // OK: direct-initialization selects B::B(int)
    B b3 {4, 5};   // OK: direct-list-initialization selects B::B(int, int)
//  B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int)
    B b5 = (B)1;   // OK: explicit cast performs static_cast
}

Ve výchozím nastavení je konstruktor je implicit, takže máte dvě způsob, jak inicializovat:

A a1 = 1;        // this is copy initialization
A a2(2);         // this is direct initialization

A tím, že definuje strukturu, jak explicitjen budete mít jeden způsob, jak direct:

B b2(2);        // this is direct initialization
B b5 = (B)1;    // not problem if you either use of assign to initialize and cast it as static_cast
Odpovězeno 16/06/2018 v 15:02
zdroj uživatelem

hlasů
0

To je z programovacího jazyka C ++ Bjarne Stroustrup:

Inicializační S = je považován za inicializace kopie . V zásadě kopii inicializátor (předmět jsme kopírování z) se umístí do inicializační objekt. Nicméně takové kopie může být optimalizována pryč (zmenšován), a krok operace (na základě move sémantiky) může být použit v případě, že je Inicializátor rvalue. Vynechávání = dělá inicializace explicitní. Explicitní inicializace je známá jako přímé inicializaci .

Odpovězeno 11/10/2019 v 06:05
zdroj uživatelem

hlasů
0

Mnoho z těchto případů se vztahují k realizaci objektu, takže je těžké dát konkrétní odpověď.

Vezměme si případ

A a = 5;
A a(5);

V tomto případě se za předpokladu správného operátor přiřazení a inicializace konstruktor, který přijímá jediný argument celého čísla, jak mohu implementovat tyto metody ovlivňuje chování každého řádku. Je běžnou praxí, ale pro jednu z těch volání druhý při provádění s cílem odstranit duplicitní kód (i když v případě tak jednoduchého jako to tam by byl žádný skutečný smysl.)

Edit: Jak je uvedeno v jiných reakcí, bude první řádek ve skutečnosti volání kopírovacího konstruktoru. Zvážit připomínky týkající se operátor přiřazení jako chování vztahující se k samostatnému přiřazení.

To znamená, že, jak kompilátor optimalizuje kód pak bude mít svůj vlastní dopad. Mám-li iniciační konstruktor voláním „=“ operátor - v případě, že kompilátor neposkytuje žádné optimalizace, horní linie by pak provést 2 skoky na rozdíl od jednoho do spodním řádku.

Nyní, pro většinu běžných situací, váš kompilátor optimalizuje prostřednictvím těchto případů a eliminovat tento druh neúčinnosti. Tak efektivně všechny různé situace byste popsat dopadne stejně. Chcete-li vidět přesně to, co se děje, se můžete podívat na objektového kódu nebo montážní výstup kompilátoru.

Odpovězeno 26/06/2009 v 22:52
zdroj uživatelem

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more