2012-06-21 23 views
8

Supponiamo di averePassando una funzione superclasse come parametro di template non typename

struct A{ 
    void f(){} 
}; 


struct B:public A{ 
}; 


template<typename C,void (C::*f)()> 
struct Call{ 

    void operator()(C* c){ 
    (c->*f)(); 
    } 
}; 

Perché

int main(){ 
    void (B::*f)()=&B::f;  
} 

lavoro, ma

Call<B,&B::f> a; 

non, lamentando

could not convert template argument ‘&A::f’ to ‘void (B::*)() 

?

(Call<A,&A::f> funziona chiaramente)

In modo simile

const void (B::*f)()=&B::f; 

cannot convert ‘void (A::*)()’ to ‘const void (B::*)()’ in initialization 

risposta

4
void (B::*f)()=&B::f; 

opere perché conversione implicita da

void (A::*f)() 

a

void (B::*f)() 

viene applicato.

4,11 (2)

Un prvalue di tipo “puntatore a membro B di tipo cv T”, dove B è un tipo di classe, può essere convertito in un prvalue di tipo “puntatore a membro di D di tipo cv T”, dove D è una classe derivata (clausola 10) di B.

Tuttavia, lo standard non permette conversioni per puntatore alla funzione di membro di argomenti del template eccezione della conversione nullptr_t:

14.3.2

Per un parametro modello non di tipo del puntatore del tipo alla funzione membro, se l'argomento modello è di tipo std :: nullptr_t, viene applicata la conversione del puntatore membro nullo (4.11); in caso contrario, non si applica la conversione . Se l'argomento template rappresenta un insieme di funzioni membro sovraccaricate, la funzione membro corrispondente viene selezionata dall'insieme (13.4).

+0

Grande, grazie! A proposito, sai anche che const void (B :: * f)() = &B::f; non è accettato? –

+0

@Fabio: void (B :: * const f)() = & B :: f funziona bene :). const void (B :: * f)() significa puntatore al metodo che restituisce il valore const void, quindi le firme dei metodi sono mismatch in quest'ultimo caso. – user396672

+0

scusa, hai ragione, grazie! –

0

Gli stati di errore esattamente ciò che è sbagliato, e void (A::*)()void (B::*)() sono diversi tipi.

Mentre in questo caso sembra che sia facile da fare, il caso generale diventa molto più complesso. Pensa a cosa succederebbe se lo A avesse diverse funzioni virtuali e il numero B avesse ereditarietà multipla. I puntatori alle funzioni dei membri sono bestie molto complesse perché devono rendere conto di quel genere di cose. Date un'occhiata a http://blogs.msdn.com/b/oldnewthing/archive/2004/02/09/70002.aspx

Si potrebbe cambiare B a:

struct B:public A{ 
    void f() { A::f(); } 
}; 

In modo che B::f() esiste realmente. Al momento B::f() è un alias di A::f() che è ovviamente di tipo void (A::*)() e non void (B::*)()

+0

Sono completamente d'accordo sui vostri punti. La mia meraviglia è perché è possibile avere il vuoto (B :: * f)() = &B::f; allora. Incidentalmente, const void (B :: * f)() = &B::f; non è accettato anche –

+0

@FabioDallaLibera Esistono diverse regole: i parametri del modello devono essere dello stesso tipo mentre l'operatore di assegnazione può utilizzare i tipi convertibili. Non ti aspetteresti 'std :: vector a; std :: vector b; a = b; 'funziona solo perché un' char' può essere implicitamente convertito in un 'int'. – IronMensan

Problemi correlati