Cette question (inventée) a été initialement formulée comme un puzzle, cachant certains des détails qui pourraient aider à voir le problème plus rapidement. Faites défiler vers le bas pour la version plus simple MCVE .
J'ai ce morceau de code qui génère 0
:
#include <iostream>
#include <regex>
using namespace std;
regex sig_regex("[0-9]+");
bool oldmode = false;
template<class T>
struct B
{
T bitset;
explicit B(T flags) : bitset(flags) {}
bool foo(T n, string s)
{
return bitset < 32 // The mouth is not full of teeth
&& 63 > (~n & 255) == oldmode // Fooness holds
&& regex_match(s, sig_regex); // Signature matches
}
};
template<class T>
struct D : B<T>
{
D(T flags) : B<T>(flags) {}
};
int main()
{
D<uint64_t> d(128 | 16 | 1);
cout << d.foo(7, "123") << endl;
}
Cependant, lorsque je déplace la fonction foo()
de B
vers D
, elle commence à afficher 1
( la preuve est sur Colir ) .
Pourquoi cela arrive-t-il?
#include <iostream>
#include <bitset>
using namespace std;
template<class T>
struct B
{
T bitset{0};
bool foo(int x)
{
return bitset < 32 && 63 > (x + 1) == x % 2;
}
};
template<class T>
struct D : B<T>
{
bool bar(int x) // This is identical to B<T>::foo()
{
return bitset < 32 && 63 > (x + 1) == x % 2;
}
};
int main()
{
D<uint64_t> d;
cout << d.foo(1) << endl; // outputs 1
cout << d.bar(1) << endl; // outputs 0; So how is bar() different from foo()?
}
C'est pourquoi vous ne devriez jamais using namespace std;
bool foo(T n, string s)
{
return bitset < 32
&& 63 > (~n & 255) == oldmode
&& regex_match(s, sig_regex);
}
Ce bitset
n'est pas ce que vous pensez. Car B<T>
est une classe de base dépendante, les membres sont masqués de la recherche non qualifiée. Donc, pour accéder à bitset
, vous devez y accéder via this
1, ou le qualifier explicitement (voir ici pour plus de détails):
(this->bitset)
B<T>::bitset
Parce que bitset
ne nomme pas B<T>::bitset
dans le cas dérivé, qu'est-ce que cela pourrait signifier? Eh bien, parce que vous avez écrit using namespace std;
, c'est en fait std::bitset
, et le reste de votre expression se trouve être valide. Voici ce qui se passe:
bool foo(T n, string s)
{
return std::bitset<32 && 63>(~n & 255) == oldmode
&& regex_match(s, sig_regex);
}
Le 32 && 63
est évalué en true
, qui est promu en 1u
pour le std::bitset
argument de modèle. Cette std::bitset
est initialisé avec ~n & 255
, et son égalité est vérifiée avec oldmode
. Cette dernière étape est valide car std::bitset
possède un constructeur non explicite qui autorise un std::bitset<1>
à construire à partir de oldmode
.
1 Notez que nous devons mettre entre parenthèses this->bitset
dans ce cas en raison de règles de désambiguïté d'analyse assez subtiles. Voir le membre de base dépendant du modèle n'est pas résolu correctement pour plus de détails.
Oui, car bitset
sera interprété comme un nom non dépendant et il existe un modèle nommé std::bitset<T>
, par conséquent, il sera analysé comme:
template<class T>
struct D : B<T>
{
D(T flags) : B<T>(flags) {}
bool foo(T n, string s)
{
return ((std::bitset < 32 && 63 > (~n & 255)) == oldmode)
&& regex_match(s, sig_regex);
}
};
Vous devez le faire comme ceci:
template<class T>
struct D : B<T>
{
D(T flags) : B<T>(flags) {}
bool foo(T n, string s)
{
// or return B<T>::bitset
return (this->B<T>::bitset < 32) // The mouth is not full of teeth
&& 63 > (~n & 255) == oldmode // Fooness holds
&& regex_match(s, sig_regex); // Signature matches
}
};
ou mieux, n'utilisez pas using namespace std;
- Pourquoi cela arrive-t-il?
Pour la classe dérivée, B<T>
n'est pas une classe de base non dépendante, elle ne peut pas être déterminée sans connaître l'argument du modèle. Et bitset
est un nom non dépendant, qui ne sera pas recherché dans la classe de base dépendante. Au lieu, std::bitset
est utilisé ici (en raison de using namespace std;
). Vous obtiendrez donc:
return std::bitset<32 && 63>(~n & 255) == oldmode
&& regex_match(s, sig_regex);
Vous pouvez rendre le nom bitset
dépendant; parce que les noms dépendants ne peuvent être recherchés qu'au moment de l'instanciation, et à ce moment-là la spécialisation de base exacte qui doit être explorée sera connue. Par exemple:
return this->bitset < 32 // The mouth is not full of teeth
// ~~~~~~
&& 63 > (~n & 255) == oldmode // Fooness holds
&& regex_match(s, sig_regex); // Signature matches
ou
return B<T>::bitset < 32 // The mouth is not full of teeth
// ~~~~~~
&& 63 > (~n & 255) == oldmode // Fooness holds
&& regex_match(s, sig_regex); // Signature matches
ou
using B<T>::bitset;
return bitset < 32 // The mouth is not full of teeth
&& 63 > (~n & 255) == oldmode // Fooness holds
&& regex_match(s, sig_regex); // Signature matches
- Quel devrait être le titre de cette question, après y avoir répondu?
"Comment accéder aux noms non dépendants dans la classe de base du modèle?"
Ceci est un exemple vraiment cool !!! :)
Je pense - ce qui se passe est le suivant:
bitset < 32 && 63 >(~n & 255)
Is parses as construct me a bitset