Domanda

Se un C ++ (implicita o esplicita) Valore costruttore accetta il suo parametro (s) per valore o riferimento a const, quando è necessario memorizzare una copia della tesi (s) nel suo oggetto in entrambi i casi?

Qui è l'esempio più breve mi viene in mente:

struct foo {
    bar _b;
    foo(bar [const&] b) // pass by value or reference-to-const?
        : _b(b) { }
};

L'idea qui è che voglio minimizzare le chiamate al costruttore di copia del bar quando viene creato un oggetto foo, in uno dei vari modi in cui potrebbe avere creato un oggetto foo.

Si prega di notare che io conosco un po 'di copia elision e (nome) Return Value Optimization, e ho letto "Voglia di velocità? passaggio per valore" , tuttavia non credo che l'articolo affronta direttamente questo caso d'uso.

Modifica:. che dovrei essere più specifico

Si supponga che non posso sapere il sizeof(bar), o se o non bar è un elemento fondamentale, tipo built-in (bar può essere un parametro di template, e foo può essere un modello di classe invece di una classe). Inoltre, non date per scontato costruttore che di foo può essere inline (o bar di, per quella materia). Non per scontato che io almeno potrebbe utilizzare un compilatore che implementa RVO.

Quello che vorrei è che ci sia una possibilità (dato ottimizzazioni del compilatore) che una chiamata del genere invocherà nessuna chiamata a costruttore di copia di bar di sorta (anche durante l'esecuzione di _b(b) nella lista di inizializzazione del foo):

foo f = function_that_creates_and_returns_a_bar_object_using_rvo();

C'è la possibilità (dato lo standard C ++ 98) che questo può essere fatto, e in caso affermativo, è più o meno probabilità di funzionare se foo accetta il suo parametro con riferimento a const anziché per valore?

È stato utile?

Soluzione

In C ++ 98 e C ++ 03, si dovrebbe passare const& bar e quindi copiare. In C ++ 0x, si dovrebbe passare bar e poi fare una mossa (fornito bar ha un costruttore mossa).

#include <utility>

struct foo
{
    bar _b;

    foo(bar b) : _b(std::move(b)) {}
};

Se si costruisce foo con un parametro lvalue, il costruttore di copia sarà chiamato a creare una b copia, e che copia verrà spostato in _b. Se si costruisce foo con un parametro rvalue, costruttore mossa di bar sarà chiamato a muoversi in b, e poi si sarà spostato di nuovo in _b.

Altri suggerimenti

A parità, passo da const e per classi sufficientemente complesse, e rapporto POD e oggetti semplici.

posa i pro / contro il passaggio per riferimento const anziché passaggio tradizionale valore

positivi:

  • Evita una copia (grande vantaggio per gli oggetti con una copia costoso)
  • Accesso in sola lettura

Negativi:

  • Qualcuno può const lanciare il const fuori il riferimento se volevano veramente

Ancora più importante con i lati positivi è esplicitamente di controllo quando la copia avviene (nel tuo caso quando passa l'inizializzazione _b nella tua lista di inizializzazione). Pensando gli aspetti negativi ... Sono d'accordo che è un rischio. Credo che quasi tutti i buoni programmatori si sentiranno sporco sulla const_cast emplying. Inoltre è possibile doverosamente cercare const_cast e gettare i pomodori la persona gettando il const fuori l'argomento. Ma hey non si sa mai e che ha il tempo di guardare il codice come un falco:?)

La mia opinione personale è che nelle classi sufficientemente complessi e in ambienti in cui la prestazione conta il vantaggio di evitare il costruttore di copia superi il rischio. Tuttavia per le classi veramente stupido e POD, tendo a fare copie dei dati e di passaggio per valore.

domanda .

Usa const T & arg se sizeof(T)>sizeof(void*) e utilizzare T arg se sizeof(T) <= sizeof(void*). Tutti i tipi fondamentali dovrebbero essere l'eccezione a questa regola

Stilisticamente, direi che il passaggio per riferimento è il modo migliore.

Se la prestazione è importante, allora non indovinare. Misurarla.

Non ho controllato quello che dice la norma, ma per provare questo empiricamente posso dirvi che GCC non ottimizza la copia via, indipendentemente dal fatto che il costruttore accetta l'argomento per valore o per riferimento const.

Se, tuttavia, il costruttore prende un riferimento const, si riesce a evitare una copia inutile quando si crea pippo da una esistente bar oggetto.

Per riassumere:

Bar b = makeBar();         // No copy, because of RVO
FooByValue f1 = makeBar(); // Copy constructor of Bar called once
FooByRef f2 = makeBar();   // Copy constructor of Bar called once
FooByValue f3(b);          // Copy constructor of Bar called twice
FooByRef f4(b);            // Copy constructor of Bar called only once

Non è che io sono un esperto di compilatore, ma credo che ha senso che in genere non può RVO un valore di ritorno in un luogo arbitrario (come ad esempio il campo membro del pippo oggetto). Invece, richiede l'obiettivo di essere in cima alla pila.

Sto assumendo bar ha una copia normale costruttore del form bar(const bar &b)

Prendere un riferimento const qui. il costruttore di bar sarà poi prendere quella di riferimento, ed eseguire la copia. copie totali: 1.

Se avete lasciato il riferimento al largo, il compilatore potrebbe fare una copia di B, passa la copia al costruttore di foo, che sarebbe poi passarlo al bar e copiare quello.

Onestamente, per questo caso, l'opzione migliore è rendere il vostro inline costruttore, il costruttore disponibile bar non genera. Poi basta passare per riferimento.

Inoltre, come si sospetta, il tuo articolo non si applica qui.

Tenere uno shared_pointer al bar nella tua classe e passarlo come tale. In questo modo non hai mai chiamare il costruttore di copia:)

.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top