2015-06-06 26 views
8

In java, per eseguire una funzione che restituisce un oggetto dello stesso tipo di un parametro ed estende una determinata classe, digitare:equivalente in C++ dell'uso di <T estende Classe> per un parametro java/tipo di ritorno

public <T extends MyClass> T foo(T bar) {...} 

Esiste un equivalente C++ di questo?

In altre parole, come si crea una funzione che accetta una classe che estende una determinata classe e restituisce lo stesso tipo? (Questo è per lo scopo di classi virtuali astratte/pure).

+0

Questa è la natura dei modelli. – AndyG

+0

Java e C++ generico sembrano essere molto diversi. –

+0

@AndyG Che cos'è esattamente un modello e come utilizzarlo in questo scenario? – ricky3350

risposta

8

Possiamo usare enable_if qui se si dispone di C++ 11 o superiore a vostra disposizione

template<typename T, typename std::enable_if<std::is_base_of<MyClass, T>::value>::type* = nullptr> 
T Foo(T bar) 
{ 
    return T(); 
} 

Ad esempio:

class MyClass 
{ 
public: 
    int a = 1; 
}; 

class Derived : public MyClass 
{ 
public: 
    int b = 2; 
}; 

class NotDerived 
{ 
public: 
    int b = 3; 
}; 

template<typename T, typename std::enable_if<std::is_base_of<MyClass, T>::value>::type* = nullptr> 
T Foo(T bar) 
{ 
    return T(); 
} 

int main() 
{ 
    Derived d; 
    NotDerived nd; 
    std::cout << Foo(d).b << std::endl;; // works 
    //std::cout << (Foo(nd)).b << std::endl;; //compiler error 

    return 0; 
} 

Live Demo

+0

#include deve essere aggiunto per std :: enable_if e std :: is_base_of. – user1056903

+0

@ user1056903: Il '#include ' che ho nella demo è sufficiente, e probabilmente preferito. – AndyG

0

Dal momento che non posso commentare la risposta accettata, sto fornendo una nuova risposta che si basa su di esso.

I parametri del modello possono essere semplificati se la condizione enable_if diventa default type template parameter anziché nullptr.

template<typename T, typename = std::enable_if<std::is_base_of<MyClass, T>::value>> 
8

Tecnicamente, come le altre risposte mostrano, ci sono modi per limitare a sottotipi di un certo tipo in fase di compilazione. Tuttavia, la maggior parte delle volte, devi semplicemente fare

template <typename T> T foo(T bar) {...} 

senza specificare un limite.

In Java, i limiti sono necessari per i generici perché la classe generica o il metodo è compilato separatamente da qualsiasi utilizzo di esso. Classi o metodi generici vengono compilati una volta, in una singola versione nel bytecode, una singola versione in grado di gestire qualsiasi argomento che i chiamanti lanciano su di esso che soddisfano i limiti della sua dichiarazione.

Il compilatore deve digitare-controllo utilizza del tipo T nel corpo del metodo, come chiamate di metodo, campo di accessi, ecc, senza sapere che cosa T è, quindi è necessario fornire un tenuti in modo che il compilatore può essere soddisfatto per esempio una chiamata al metodo è valida perché è definita su tutti i tipi che soddisfano quel limite. Ad esempio, se nel corpo del metodo è presente l'espressione bar.baz(), il compilatore ti consente di compilare solo se il tipo MyClass (e quindi tutti i sottotipi di esso) fornisce il metodo .baz(); se non avessi fornito limiti, il compilatore si lamenterebbe che lo Object (il limite superiore implicito) non ha alcun metodo .baz().

I modelli C++ sono diversi. La classe o la funzione basata su modello viene "istanziata" (compilata di nuovo) per ogni argomento di tipo diverso per cui è usato. Quindi, al momento di compilare il corpo della funzione per un particolare T, il compilatore sa cosa è T ed è in grado di controllare manualmente gli usi di quel tipo.

Quindi se si avesse l'espressione bar.baz() nel corpo della funzione, ciò andrebbe bene. Se si è utilizzata questa funzione con T come tipo che estende MyClass, verrà compilato correttamente, poiché tale tipo ha un .baz().Se usi questa funzione con un tipo che non ha un .baz(), allora non riuscirà a compilare a quell'utilizzo di esso. Se utilizzi accidentalmente la funzione con un tipo che non si estende MyClass ma ha un .baz() i cui tipi di parametri e tipo di reso corrispondono al modo in cui lo stai utilizzando, verrà compilato anche; ma non è necessariamente una brutta cosa. I modelli C++ non vengono solitamente utilizzati con le gerarchie di tipi, ma piuttosto con i requisiti su ciò che il tipo deve fornire. Quindi, per esempio, un algoritmo di ordinamento non richiederà che il suo contenitore e/o tipo di elemento estendano un certo tipo, ma piuttosto che il contenitore fornisca alcune caratteristiche (ad esempio un operatore di accesso casuale) e il tipo di elemento fornisca alcune caratteristiche (es. un operatore minore di).