2015-11-04 20 views
9

Ho scritto del codice S s; ... s = {};, aspettandomi che finisse come lo S s = {};. Tuttavia non lo fece. Il seguente esempio riproduce il problema:Risoluzione di sovraccarico: assegnazione di parentesi graffe vuote

#include <iostream> 

struct S 
{ 
    S(): a(5) { } 
    S(int t): a(t) {} 

    S &operator=(int t) { a = t; return *this; } 
    S &operator=(S const &t) = default; 

    int a; 
}; 

int main() 
{ 
    S s = {}; 

    S t; 
    t = {}; 

    std::cout << s.a << '\n'; 
    std::cout << t.a << '\n'; 
} 

L'output è:

5 
0 

Le mie domande sono:

  1. Perché operator=(int) selezionato qui, invece di "ambiguo" o l'altro ?
  2. C'è una soluzione ordinata, senza cambiare S?

Il mio intento è s = S{};. Scrivere s = {}; sarebbe conveniente se funzionasse. Attualmente sto usando s = decltype(s){}; ma preferirei evitare di ripetere il tipo o il nome della variabile.

+0

'{}' a 'int' è la conversione dell'identità ([\ [over.ics.list \]/9] (http://eel.is/c++draft/over.ics.list#9)). '{}' a 'S' è una conversione definita dall'utente ([\ [over.ics.list \]/6] (http://eel.is/c++draft/over.ics.list#6)). Giuro di aver visto una domanda molto simile ieri ... –

+0

@ T.C. se è così, credo che avrei potuto risparmiarmi qualche tempo di sviluppo leggendo più domande ieri! –

+0

Mi sembra di aver commesso lo stesso errore in molti punti del mio codice ... ad es. Di solito uso 'void clear() {* this = {}; } 'con l'intento di ripristinare un oggetto al suo stato inizializzato dal valore –

risposta

8

Perché operator=(int) selezionato qui, invece di "ambiguo" o l'altro?

{} al int è la conversione di identità ([over.ics.list]/9).{} a S è una conversione definita dall'utente ([over.ics.list]/6) (tecnicamente, è {} a const S& e passa prima [over.ics.list]/8 e [over.ics.ref] prima di tornare a [over.ics. list]/6).

Le prime vittorie.

C'è una soluzione ordinata?

Una variante del trucco std::experimental::optional tira rendere t = {} rendere sempre t vuoto. Il tasto è di rendere operator=(int) un modello. Se si desidera accettare int e solo int, allora diventa

template<class Int, std::enable_if_t<std::is_same<Int, int>{}, int> = 0> 
S& operator=(Int t) { a = t; return *this; } 

diversi vincoli possono essere utilizzati se si desidera attivare le conversioni (si sarebbe probabilmente anche vuole prendere l'argomento di riferimento in questo caso).

Il punto è che rendendo il tipo dell'operando di destra un parametro di modello, è possibile bloccare t = {} dall'utilizzo di questo overload, poiché {} è un contesto non dedotto.

... senza modificare S?

e quindi s = default_constructed_instance_of(s); conteggio?

+0

Heh, riguardo l'ultima riga che ho seguito con' modello reset nullo (T & t) {t = T {}; } ' –

-1

Prima di tutto, il caso non ha nulla a che fare con la versione "int" dell'operatore di assegnazione, è sufficiente eliminarlo. È possibile eliminare anche l'altro operatore di assegnazione in quanto verrà generato dal compilatore. IE questo tipo di tipo riceve automaticamente i costruttori copia/sposta e l'operatore di assegnazione. (Cioè non sono vietati e si sta solo ripetendo quello che il compilatore fa automaticamente con la notazione esplicito)

Il primo caso

utilizza copia di inizializzazione:

S s = {};  // the default constructor is invoked 

Questo è un post -costruzione copia di costruzione, ma i compilatori ottimizzano casi così semplici. Si dovrebbe usare direzione di inizializzazione invece:

S s{};  // the default constructor is invoked (as you have it) 

Nota, è possibile anche scrivere:

S s;   // the default constructor is invoked if you have it 

Il secondo caso

cosa si dovrebbe scrivere è inizializzazione diretta del lato destro dell'assegnazione delle copie:

Questa notazione invocherà il costruttore predefinito (se ce n'è uno), o l'inizializzazione del valore per i membri (purché il tipo sia un aggregato). Ecco le informazioni pertinenti: http://en.cppreference.com/w/cpp/language/value_initialization

+0

Non riesco ancora a ottenere quello che sta facendo il suo secondo caso (come scritto) – pm100

+1

se rimuovo la versione "int" dell'operatore di assegnazione, quindi l'output è '5 5' –