Domanda

la mia domanda di oggi è abbastanza semplice:perché non può il compilatore di dedurre i parametri del modello da costruttori di classe, tanto quanto si può fare da parametri di una funzione?Per esempio, perché non il codice riportato di seguito è valido:

template<typename obj>
class Variable {
      obj data;
      public: Variable(obj d)
              {
                   data = d;
              }
};

int main()
{
    int num = 2;
    Variable var(num); //would be equivalent to Variable<int> var(num),
    return 0;          //but actually a compile error
}

Come ho detto, ho capito che questo non è valido, quindi la mia domanda è perché non è vero?Permettendo che questo creare qualsiasi grande sintattica fori?C'è un caso in cui uno non vorrebbe questa funzionalità (in cui la deduzione di un tipo di causare problemi)?Sto solo cercando di capire la logica dietro permettendo modello di inferenza per le funzioni, ma non per opportunamente costruito classi.

È stato utile?

Soluzione

Credo che non è valido perché il costruttore non è sempre l'unico punto di ingresso della classe (sto parlando di costruttore di copia e l'operatore =). Quindi supponiamo che si sta utilizzando la classe in questo modo:

MyClass m(string s);
MyClass *pm;
*pm = m;

Non sono sicuro se sarebbe così evidente per il parser di sapere che tipo modello è il pm MyClass;

Non sono sicuro se quello che ho detto ha senso, ma sentitevi liberi di aggiungere qualche commento, questa è una domanda interessante.

C ++ 17

E 'accettato che C ++ 17 avrà tipo deduzione dal argomenti del costruttore.

Esempi:

std::pair p(2, 4.5);
std::tuple t(4, 3, 2.5);

accettati carta .

Altri suggerimenti

Non si può fare quello che chiedete per ragioni diverse persone hanno affrontato, ma si può fare questo:

template<typename T>
class Variable {
    public: Variable(T d) {}
};
template<typename T>
Variable<T> make_variable(T instance) {
  return Variable<T>(instance);
}

, che per tutti intenti e le finalità è la stessa cosa che chiedi. Se ami l'incapsulamento si può fare make_variable una funzione membro statica. Questo è quello che la gente chiama nome del costruttore. Quindi non solo non fare quello che vuoi, ma è quasi chiama ciò che si vuole:. Il compilatore è infering il parametro di template dal costruttore (nome)

NB: qualsiasi compilatore ragionevole ottimizzerà via l'oggetto temporaneo quando si scrive qualcosa come

Variable<T> v = make_variable(instance);

In età illuminata del 2016, con due nuovi standard sotto la nostra cintura dal momento che questa domanda è stato chiesto e uno nuovo appena dietro l'angolo, la cosa fondamentale da sapere è che i compilatori di supporto C++17 standard compilare il codice così com'è.

Modello argomento detrazione per modelli di classe in C++17

Qui (per gentile concessione di una modifica da Olzhas Zhumabek del accettato la risposta) è la carta di dettaglio le modifiche apportate allo standard.

Affrontare i problemi da altre risposte

L'attuale top-rated risposta

Questa risposta indica che "il costruttore di copia e operator="non saprei il modello corretto specializzazioni.

Questa è una sciocchezza, perché la norma-copy constructor e operator= esistono solo per un noto tipo di modello:

template <typename T>
class MyClass {
    MyClass(const MyClass&) =default;
    ... etc...
};

// usage example modified from the answer
MyClass m(string("blah blah blah"));
MyClass *pm;   // WHAT IS THIS?
*pm = m;

Qui, come ho notato nei commenti, c'è nessun motivo per MyClass *pm per essere una dichiarazione legale con o senza la nuova forma di inferenza: MyClass non è un tipo di (si tratta di un modello), quindi non ha senso dichiarare un puntatore di tipo MyClass.Ecco un modo per correggere l'esempio:

MyClass m(string("blah blah blah"));
decltype(m) *pm;               // uses type inference!
*pm = m;

Qui, pm è già del tipo corretto, e quindi l'inferenza è banale.Inoltre, è impossibile accidentalmente mix tipi di quando si chiama il copy-constructor:

MyClass m(string("blah blah blah"));
auto pm = &(MyClass(m));

Qui, pm sarà un puntatore a una copia di m.Qui, MyClass è in fase di copia-costruito da m—che è di tipo MyClass<string> (e non del tipo inesistente MyClass).Così, nel punto in cui pm's tipo viene dedotto, c' è informazioni sufficienti per sapere che il modello-tipo di m, e quindi il modello-tipo di pm, è string.

Inoltre, il seguente sempre sollevare un errore di compilazione:

MyClass s(string("blah blah blah"));
MyClass i(3);
i = s;

Questo perché la dichiarazione del costruttore di copia è non basato su modelli:

MyClass(const MyClass&);

Qui, il copia-argomento del costruttore modello tipo partite il modello-tipo della classe nel suo complesso;cioè, quando MyClass<string> viene creata un'istanza, MyClass<string>::MyClass(const MyClass<string>&); viene creata un'istanza con esso, e quando MyClass<int> viene creata un'istanza, MyClass<int>::MyClass(const MyClass<int>&); viene creata un'istanza.A meno che non sia esplicitamente specificato o un templatized costruttore è dichiarato, non c'è motivo per il compilatore per creare un'istanza di MyClass<int>::MyClass(const MyClass<string>&);, che sarebbe, ovviamente, essere inappropriato.

La risposta da Cătălin Pitiș

Pitiș dà un esempio dedurre Variable<int> e Variable<double>, poi afferma:

Ho lo stesso nome del tipo (Variabile) il codice per i due diversi tipi di Variabile e Variabile).Dal mio punto di vista soggettivo, esso influisce sulla leggibilità del codice, praticamente.

Come indicato nell'esempio precedente, Variable di per sé è non un tipo di nome, anche se la nuova funzionalità rende l'aspetto dal punto di vista sintattico.

Pitiș poi si chiede cosa sarebbe successo se nessun costruttore che avrebbe permesso l'inferenza appropriato.La risposta è che nessuna deduzione è consentita, perché l'inferenza è attivato dal chiamata al costruttore.Senza un costruttore di chiamata, c'è nessuna illazione.

Questo è simile a chiedere qual è la versione di foo lo si evince da qui:

template <typename T> foo();
foo();

La risposta è che questo codice è illegale, per il motivo indicato.

MSalter risposta

Questo è quanto posso dire, l'unica risposta a portare una legittima preoccupazione circa la funzione di proposta.

L'esempio è:

Variable var(num);  // If equivalent to Variable<int> var(num),
Variable var2(var); // Variable<int> or Variable<Variable<int>> ?

La questione chiave è, fa il compilatore a selezionare il tipo dedotto costruttore di qua o di la copia costruttore?

Cercando il codice, possiamo vedere che il costruttore di copia viene selezionato. Per espandere l'esempio:

Variable var(num);          // infering ctor
Variable var2(var);         // copy ctor
Variable var3(move(var));   // move ctor
// Variable var4(Variable(num));     // compiler error

Io non sono sicuro di come la proposta e la nuova versione della norma specificare questo;sembra essere determinato da "deduzione guide", che sono un nuovo bit di standardese che non ho ancora capito.

Io non sono anche sicuro perché l' var4 la detrazione è illegale;l'errore del compilatore g++ sembra indicare che l'istruzione viene analizzato come una dichiarazione di funzione.

Manca ancora: Rende il seguente codice piuttosto ambiguo:

int main()
{
    int num = 2;
    Variable var(num);  // If equivalent to Variable<int> var(num),
    Variable var2(var); //Variable<int> or Variable<Variable<int>> ?
}

Supponendo che il compilatore supporta quello che hai chiesto. Allora questo codice è valido:

Variable v1( 10); // Variable<int>

// Some code here

Variable v2( 20.4); // Variable<double>

Ora, io ho lo stesso nome del tipo (variabile) nel codice per due tipi diversi (variabili e variabile). Dal mio punto di vista soggettivo, colpisce la leggibilità del codice più o meno. Avere lo stesso nome tipo per due tipi differenti nello stesso spazio dei nomi sembra fuorviante per me.

aggiornamento successivo: Un'altra cosa da considerare:. Parziale (o totale) del modello di specializzazione

Che cosa succede se mi specializzo variabile e fornire nessun costruttore come ci si aspetta?

Quindi, vorrei avere:

template<>
class Variable<int>
{
// Provide default constructor only.
};

Poi ho il codice:

Variable v( 10);

Cosa dovrebbe fare il compilatore fare? Uso generico definizione di classe variabile per dedurre che è variabile, allora scoprire che variabile non fornisce un costruttore parametro?

Il C ++ 03 e lo standard C ++ 11 non consente per il modello argomento deduzione dai parametri passati al constuructor.

Ma c'è una proposta di "parametro detrazione Template per i costruttori" così si può ottenere quello che stai chiedendo al più presto. Modifica:. Infatti, questa caratteristica è stata confermata per il C ++ 17

Si veda: http: // www. open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3602.html e http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0091r0.html

Un sacco di classi non dipendono da parametri del costruttore. Ci sono solo un paio di classi che hanno un solo costruttore, e parametrizzare in base al tipo (s) di questo costruttore.

Se si ha realmente bisogno di modello di inferenza, utilizzare una funzione di supporto:

template<typename obj>
class Variable 
{
      obj data;
public: 
      Variable(obj d)
      : data(d)
      { }
};

template<typename obj>
inline Variable<obj> makeVariable(const obj& d)
{
    return Variable<obj>(d);
}

La deduzione di tipi è limitato a funzioni modello in corrente C ++, ma è stato a lungo capito che tipo di detrazione in altri contesti sarebbe molto utile. Quindi auto di C ++ 0x.

Mentre esattamente quello che suggeriscono non sarà possibile in C ++ 0x, il seguente mostra si può ottenere molto vicino:

template <class X>
Variable<typename std::remove_reference<X>::type> MakeVariable(X&& x)
{
    // remove reference required for the case that x is an lvalue
    return Variable<typename std::remove_reference<X>::type>(std::forward(x));
}

void test()
{
    auto v = MakeVariable(2); // v is of type Variable<int>
}

Hai ragione il compilatore potrebbe facilmente intuire, ma non è nello standard o C ++ 0x per quanto ne so quindi dovrete aspettare atleast 10 più anni (norme ISO volta fissata intorno rate) prima di fornitori compiller aggiungere questa funzionalità

Vediamo il problema con riferimento ad una classe di tutti dovrebbero essere familiarità con - std :: vector.

In primo luogo, un uso molto comune di vettore è di utilizzare il costruttore senza parametri:

vector <int> v;

In questo caso, ovviamente, non può essere eseguita inferenza.

Un secondo uso comune è quello di creare un vettore pre-size:

vector <string> v(100);

Qui, se sono stati utilizzati inferenza:

vector v(100);

si ottiene un vettore di interi, non stringhe, e presumibilmente non è dimensionato!

Infine, considerare i costruttori che prendono più parametri - con "deduzione":

vector v( 100, foobar() );      // foobar is some class

Quale parametro deve essere utilizzato per l'inferenza? Avremmo bisogno di un modo per dire al compilatore che dovrebbe essere la seconda.

Con tutti questi problemi per una classe semplice come vettore, è facile capire perché l'inferenza non viene utilizzato.

Fare il ctor un modello la variabile può avere un solo forma , ma varie ctors:

class Variable {
      obj data; // let the compiler guess
      public:
      template<typename obj>
      Variable(obj d)
       {
           data = d;
       }
};

int main()
{
    int num = 2;
    Variable var(num);  // Variable::data int?

    float num2 = 2.0f;
    Variable var2(num2);  // Variable::data float?
    return 0;         
}

Vedi? Non possiamo avere più membri variabile :: dati.

Il C ++ Template argomento Deduzione per maggiori informazioni su questo.

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