51 votes

Le déplacement d'une fonction membre de la classe de base vers la classe dérivée rompt le programme sans raison évidente

Ce (fait) question a d'abord été formulé comme un puzzle, cacher certains détails qui pourraient aider à voir le problème plus rapidement. Faites défiler vers le bas pour la plus simple MCVE version.


D'origine (un-la de puzzle) version

J'ai ce bout 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, quand je bouge la fonction foo() de B de D il commence à sortir des 1 (la preuve est sur Coliru).

Pourquoi est-ce arrivé?


MCVE version

En direct sur Coliru

#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()?
}

90voto

TartanLlama Points 1461

C'est pourquoi vous devriez ne jamais using namespace std;

bool foo(T n, string s)
{
    return bitset < 32                  
           && 63 > (~n & 255) == oldmode 
           && regex_match(s, sig_regex);
}

Qu' bitset n'est pas ce que vous pensez qu'elle est. Parce qu' B<T> est un dépendant de la classe de base, les membres sont cachés non qualifiés de recherche. Afin d'accéder bitset, vous devez avoir accès par l'entremise this1, ou explicitement qualifier (voir ici pour plus de détails):

(this->bitset)
B<T>::bitset

Parce qu' bitset n'a pas de nom B<T>::bitset dans le dérivé cas, que pourrait-il dire? Eh bien, parce que vous avez écrit using namespace std;, std::bitset, et le reste de votre expression se trouve juste à être valide. Voici ce qui arrive:

bool foo(T n, string s)
{
    return std::bitset<32 && 63>(~n & 255) == oldmode 
           && regex_match(s, sig_regex);
}

L' 32 && 63 évalue true, ce qui est promu 1u de la std::bitset argument de modèle. Cette std::bitset est initialisé avec ~n & 255, et est vérifié pour l'égalité avec l' oldmode. Cette dernière étape est valide parce qu' std::bitset a un non-explicite du constructeur qui permet temporairement std::bitset<1> à être construit à partir d' oldmode.


1 Notez que nous devons parenthesise this->bitset dans ce cas en raison de certaines assez subtile analyse disambiguity règles. Voir Modèle dépendant de la base de membre n'est pas réglée correctement pour plus de détails.

18voto

Danh Points 4923

Oui, car bitset sera interprété comme un nom non dépendant et il existe un modèle nommé std::bitset<T> . Il sera donc analysé comme suit:

 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;

8voto

songyuanyao Points 2265
  1. Pourquoi est-ce arrivé?

Pour la classe dérivée, B<T> n'est pas un non dépendantes de la classe de base, il ne peut pas être déterminée sans connaître l'argument de modèle. Et bitset est un non dépendantes nom, qui ne sera pas regardé en dépendant de la classe de base. Au lieu de cela, std::bitset est utilisé ici (en raison de l' using namespace std;). Ainsi, vous obtiendrez:

return std::bitset<32 && 63>(~n & 255) == oldmode
       && regex_match(s, sig_regex);

Vous pourriez faire le nom d' bitset à charge; car dépendante de noms peuvent être consultées uniquement au moment de l'instanciation, et à ce moment exact de la base de la spécialisation qui doit être explorée. 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
  1. Quel devrait être le titre de cette question, après c'est une réponse?

"Comment accéder à la non dépendantes des noms dans la base de modèle de classe?"

3voto

Nim Points 22570

C'est un exemple vraiment cool !!! :)

Je pense - ce qui se passe est la suivante:

 bitset < 32 && 63 >(~n & 255)
 

Est-ce que les parses me construisent a bitset

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X