Tipo di puntatore a membro dalla classe base
-
27-09-2019 - |
Domanda
Ho un problema per quanto riguarda i puntatori membri. Il seguente codice non riesce a compilare utilizzando CC ed entrambi Oracle Solaris Studio 12.2 di Cygwin GCC 4.3.4, ma funziona con Microsoft Visual C ++ 2010:
struct A {
int x;
};
struct B : public A {
};
template<typename T> class Bar {
public:
template<typename M> void foo(M T::*p);
};
int main(int, char *[]) {
Bar<B> bbar;
bbar.foo(&B::x);
return 0;
}
Al successivo all'ultima riga entrambi i compilatori di cui sopra non riescono a trovare una corrispondenza per Bar<B>::foo(int A::*)
. Ho scritto un semplice test per confermare che il tipo di &B::x
espressione è in realtà int A::*
:
// ...
static void foo(int A::*p) {
std::cout << "A" << std::endl;
}
static void foo(int B::*p) {
std::cout << "B" << std::endl;
}
int main(int, char *[]) {
foo(&B::x); // prints "A", even on MS VC++ 2010
return 0;
}
Il seguente soluzione funziona con GCC (non testato con ancora un Oracle CC), ma non riesce con VC ++ a causa di ambiguità:
template<typename T> class Bar {
public:
template<typename M> void foo(M T::*p);
template<typename M, typename _T_base> inline void foo(M _T_base::*p) {
foo(static_cast<M T::*>(p));
}
};
La mia domanda:
Quale comportamento è corretto? A quanto pare VC ++ fa un upcast implicita da int A::*
a int B::*
per soddisfare la chiamata al modello funzione di membro, non dovrebbero gli altri due compilatori in considerazione di fare lo stesso?
Soluzione
Una conversione da int A::*
a int B::*
è consentito, e non è questo il problema. Il problema è in detrazione modello di argomentazione, come si può vedere se si tenta il seguente programma che fornisce un <int>
modello argomento per B::foo
e compila, e una funzione foo2
non membro, che produce lo stesso errore di B::foo
fatto prima.
struct A {
int x;
};
struct B : public A {
};
template <typename T> class Bar {
public:
template<typename M> void foo(M T::*p);
};
template<typename M> void foo2(M B::*p);
int main(int, char*[]) {
Bar<B> bbar;
bbar.foo<int>(&B::x);
foo2(&B::x); // error, but foo2<int>(&B::x) would work.
return 0;
}
Credo che questa situazione non è coperto dai casi in cui si suppone il compilatore di dedurre l'argomento <int>
modello da solo. 14.8.2.1p3:
In generale, i tentativi di processo deduzione per trovare valori degli argomenti di template che renderanno la dedotta Un identico ad A (dopo il tipo A si trasforma come descritto sopra). Tuttavia, ci sono tre casi che permettono una differenza:
- Se la P originale è un tipo di riferimento, il dedotta A (cioè, il tipo indicato dal riferimento) può essere più cv-qualificati di A.
- A può essere un altro puntatore o puntatore al tipo di utente che può essere convertito alla dedotta A attraverso una conversione di qualificazione (conv.qual).
- Se P è una classe, e P ha la forma modello-id, allora A può essere una classe derivata della A. dedotta Allo stesso modo, se P è un puntatore ad una classe di forma modello-id, A può essere un puntatore a una classe derivata a cui punta il dedotta a.
Ecco "P" è della funzione template tipo di argomento: M B::*p
, dove il parametro tipo di modello M
è da determinare. "A" è il tipo di argomento reale: int A::*
. P e A non sono certo un riferimento o una classe, e il tipo di conversione di puntatore-a-membro avremmo bisogno per questo di lavoro non è una conversione di qualificazione (che descrive solo const / manipolazioni volatili come X*
a const X*
o int X::*
a const int X::*
).
Quindi, l'argomento modello non può essere dedotto, e si dovrebbe aggiungere il parametro modello esplicito <int>
al codice.