Il seguente codice mostra il nucleo di una ++ motivo template metaprogrammazione C ho utilizzato per determinare se un tipo T
è un'istanza di una classe template specifica:Funzione con un argomento puntatore
#include <iostream>
template<class A, class B>
struct S{};
template<class A, class B>
constexpr bool isS(const S<A,B>*) {return true;}
template<class T>
constexpr bool isS(const T*) {return false;}
int main() {
S<int,char> s;
std::cout<<isS(&s)<<std::endl;
return 0;
}
E ' dispone di due sovraccarichi di un modello di funzione isS
e produce 1
, come previsto. Se rimuovere il puntatore dalla seconda isS
, cioè sostituirlo con
template<class T>
constexpr bool isS(const T) {return false;}
il programma stampa inaspettatamente 0
. Se entrambe le versioni di isS
passano alla fase di risoluzione del sovraccarico della compilazione, l'output implica che il compilatore stia scegliendo il secondo sovraccarico. Ho testato questo sotto GCC, Clang e vC++ usando i compilatori online here, e tutti producono lo stesso risultato. Perché succede?
Ho letto più volte l'articolo di Herb Sutter e sembra che entrambe le funzioni isS
siano considerate modelli di base. Se è così, allora è una questione di quale sia il più specializzato. Andando per intuizione e this answer, mi aspetto che il primo isS
sia il più specializzato, perché T
può corrispondere a ogni istanziazione di S<A,B>*
e ci sono molte possibili istanze di T
che non possono corrispondere a S<A,B>*
. Mi piacerebbe individuare il paragrafo nella bozza di lavoro che definisce questo comportamento, ma non sono del tutto sicuro di quale fase della compilazione stia causando il problema. È qualcosa che ha a che fare con "14.8.2.4 Dedurre gli argomenti del modello durante l'ordinamento parziale"?
Questo problema è particolarmente sorprendente dato che il codice seguente, in cui il primo isS
prende un riferimento const S<A,B>
e la seconda prende un const T
, emette il valore atteso 1
:
#include <iostream>
template<class A, class B>
struct S{};
template<class A, class B>
constexpr bool isS(const S<A,B>&) {return true;}
template<class T>
constexpr bool isS(const T) {return false;}
int main() {
S<int,char> s;
std::cout<<isS(s)<<std::endl;
return 0;
}
Così il problema sembra essere qualcosa a che fare con come vengono trattati i puntatori.
Un parametro 'const T' equivale a un parametro' T' (quindi otterrai una corrispondenza esatta) e hai bisogno di una conversione di qualifica da 'S *' a 'S const *'. Prova a usare 'S const s ;, invece nel tuo' main'. - Il test * più specializzato * si verifica molto tardi nella risoluzione di sovraccarico, se altri test non potrebbero decidere tra i sovraccarichi. Qui, possiamo selezionare il secondo in precedenza perché ha un rango migliore (corrispondenza esatta contro aggiustamento della qualifica). –
dyp
@dyp Ok, ma allora perché il compilatore sceglie il primo 'isS' nel frammento di codice in fondo, che comprende i riferimenti? Il compilatore non deve eseguire una conversione di qualifica da 'S &' a 'const S &' in questo caso? –
Ose
@Ose No, 'T *' per 'const T *' è una conversione di adattamento di qualifica, 'T' per' const T & 'è una conversione di identità come il riferimento * si collega direttamente * all'argomento. – TartanLlama