2013-10-20 7 views
12

Sto provando a creare una classe che dovrebbe ereditare i costruttori da altre classi ma senza ereditare da quelle stesse classi.Come utilizzare std :: enable_if per selezionare condizionatamente un costruttore variadic?

A un certo punto durante l'inizializzazione della mia classe, voglio utilizzare l'inoltro perfetto per creare un oggetto del tipo il cui costruttore ha abbinato gli argomenti dati.

Tranne che per il costruttore predefinito senza argomenti, non ci devono essere ambiguità.

Questo è il mio codice:

#include <string> 

using namespace std; 

//NOTE: this class is just an example to demonstrate the problem 
class String { 
    public: 
     //default constructor to prevent ambiguity 
     String() {} 

     //construct from wstring 
     template<typename... Args> 
     String(enable_if<is_constructible<wstring, Args...>::value, Args>::type&&... args) : ws(forward<Args>(args)...) {} 

     //construct from string 
     template<typename... Args> 
     String(enable_if<is_constructible<string, Args...>::value, Args>::type&&... args) : s(forward<Args>(args)...) {} 
    private: 
     string s; 
     wstring ws; 
}; 

void foo(const String& string) { 
} 

int main() 
{ 
    foo(L"123"); 
    foo("123"); 
    return 0; 
} 

Ho provato molte cose, ma non riesco proprio a farlo funzionare.

  • Nell'approccio corrente enable_if non riesce a detrarre automaticamente le args template (credo)
  • Da quando uso il costruttore non posso usare enable_if sul valore di ritorno
  • aggiunta di un altro parametro predefinito per enable_if Won 't lavoro perché il costruttore è variadic
  • Quando rimuovo enable_if dagli argomenti della funzione il compilatore si lamenta sovraccarichi su invalidi (ovviamente)

Esiste un modo elegante per risolvere questo problema?

Modifica: L'unica conversione implicita consentita dallo standard non dovrebbe verificarsi nella mia classe. [codice di esempio modificato]

Una soluzione che funziona con l'esempio precedente sarebbe definire un singolo costruttore variadic e perfezionare avanti gli argomenti di una funzione di inizializzazione condizionata. Tuttavia, vorrei evitare questo sovraccarico perché i membri devono essere costruiti di default e questo potrebbe non funzionare in altri casi.

(Sentitevi liberi di modificare la domanda se le cose possono essere fatte più chiare)

+0

Buona domanda e non ho una risposta. FWIW, * senza modelli * variadic [funziona] (http://coliru.stacked-crooked.com/a/c799960a369ccf7e) usando i parametri predefiniti. –

+0

Ho chiesto la domanda correlata anche in: http://stackoverflow.com/questions/18700072/using-sfinae-to-select-different-method-implementations-in-a-clase-template.Una risposta era anche usare la specializzazione invece :-) – Klaus

+0

Come ho detto in un commento alla soluzione di Klaus: Solo ** una ** conversione definita dall'utente viene eseguita implicitamente. Sia dalla stringa letterale a 'std :: basic_string' o da una' std :: basic_string' al tuo tipo 'String', ma non entrambi. – dyp

risposta

2

non riesco a capire il problema e anche la soluzione. Se voglio usare 2 diversi tipi per un metodo o un costruttore, posso semplicemente scrivere entrambi i tipi. Non è necessario avere un modello con SFINAE per questo motivo! Questo può semplicemente fatto per specializzazione.

class A 
{ 
    public: 
    template <typename ... Args> 
    A(const wstring &, Args ...); 

    template <typename ... Args> 
    A(const string &, Args ...); 
}; 

Per avere un modello che esatto partita per un solo tipo non è davvero semanticamente un modello :-)

Dopo aver letto il tuo commento ho ottenuto questa soluzione:

class foo 
{ 
    public: 
     template <typename ... Args> 
     foo(const string &&x, Args ...) { cout << "Using string" << endl; } 

     template <typename ... Args> 
     foo(const wstring &&x, Args ...) { cout << "Using wstring" << endl; } 
}; 

int main() 
{ 
    foo("123"); 
    foo(L"123"); 

    foo("123", 1); 
    foo(L"123", 1.11); 
    return 0; 
} 

e questo ritorno come previsto:

Using string 
Using wstring 
Using string 
Using wstring 
+0

Vero. Questo in realtà funziona nel mio caso attuale e non mi accorgo che è un po 'imbarazzante. L'unico problema è che potrebbe utilizzare una conversione implicita che, credo, stava cercando di evitare. – user1492625

+0

Una conversione implicita dovrebbe avvenire solo se il parametro non corrisponde esattamente. Come capisco il tuo esempio hai 2 tipi definiti espliciti. Inoltre, se è possibile specificare i tipi con un enable_if si ha un effetto diverso: se il tipo specificato non corrisponde esattamente non si avranno istanze di nessuno dei modelli che si traduce in un errore di compilazione. – Klaus

+0

Esiste una conversione implicita dai valori letterali stringa forniti al tipo di parametro ('wstring' o' stringa'). A proposito, questo è un esempio in cui un argomento sink di valore (invece di by-const-ref) è più performante, se non si forniscono addizionali sovraccarichi di refval. – dyp