2015-05-29 5 views
9

vorrei avere un modello con un valore nidificato che dovrebbe essere inizializzato da una data funzione di inizializzazione:modello Specializzarsi con puntatore a funzione, che dipende dal parametro modello

template <typename T, T(INIT)()> struct Foo 
{ 
    T value = INIT(); 
}; 

Può essere utilizzato in questo modo:

// Some random type only instanceable through factory() 
struct Bar 
{ 
    int bar{}; 
private: 
    // The only way to create a Bar is through factory() 
    friend Bar factory(); 
    Bar() {}; 
}; 

Bar factory() { return {}; } 

Foo<Bar, factory> foo; 

Ma, se è prevista alcuna funzione, il modello dovrebbe cercare di default-inizializzare il valore nidificato, così ho cercato di specializzarsi il modello:

template <typename T> struct Foo<T, nullptr> 
{ 
    T value{}; 
}; 

L'idea è quella di utilizzare in questo modo:

struct Baz{}; 

Foo<Bar, factory> foo; // Nested Bar have Bar::bar initialized through factory function. 
Foo<Baz>   baz; // No factory function needed, nested Baz default-initialized. 

Ma ho appena scoperto che modello di tipi di specializzazione parziali non possono contare su altri tipi di modello, l'errore che sto ricevendo è incollato sotto:

error: type 'T (*)()' of template argument 'nullptr' depends on a template parameter template struct Foo


C'è un modo per raggiungere il mio obiettivo? Sarebbe bello se funziona con le variabili di modello così:

template <typename T, T(INIT)()> T Foo = INIT(); 
template <typename T>   T Foo<T, nullptr>{}; 

domanda Extra: Perché specializzazioni parziali non possono dipendere da parametri di modello? Qual è la logica dietro questa restrizione?

risposta

2

Se si tratta solo di eseguire un'inizializzazione predefinita se manca il secondo parametro del modello, è possibile fornire una funzione di inizializzazione predefinita basata su modello come il parametro predefinito.

template<typename T> 
    T do_default_assign() { 
     return T(); 
    };                  

template <typename T, T (INIT)() = do_default_assign<T> > struct Foo 
    { 
     T value = INIT(); 
    }; 

Questa soffre però di un "ritorno per valore" inutile e operazione di assegnazione che potrebbe essere costosa o impossibile per alcuni T.

+0

Accettato questo e nessuno da @ Jarod42 perché è stato risposto 1 secondo prima! –

3

Per il vostro caso, è possibile utilizzare:

template <typename T> 
T default_construct() { return T{}; } 

template <typename T, T(INIT)() = &default_construct<T>> 
struct Foo 
{ 
    T value = INIT(); 
}; 

E quindi utilizzare le cose come:

Foo<int> f; 
Foo<int, bar> b; 

Live demo

2

È possibile definire una funzione constructor modello che inizializzare un valore di tipo Type e quindi utilizzarlo come un costruttore di default:

template<typename Type, typename... Args> 
Type constructor(Args... args) { 
    return Type(std::forward<Args>(args)...); 
} 

e quindi utilizzarlo come modello predefinito argomento per la funzione:

template <typename T, T(INIT)() = constructor<T>> struct Foo 
{ 
    T value = INIT(); 
}; 

Live demo

+0

Questo approccio sembra ottimo perché permette di passare qualsiasi parametro al costruttore, ma fornire un 'costruttore 'non compatibile con' T (INIT)() 'non è possibile quindi alla fine: inoltrare tutto il' Args' al 'costruttore 'non ha senso :( –

+0

@PaperBirdMaster True, ma ora hai un' costruttore 'più generale, che è una funzione utile che funziona su qualsiasi costruttore di qualsiasi tipo (per altri scopi), invece di un 'default_construct' hardcoded. o 'do_default_assign' come altri hanno proposto. – Shoe