Leon Leon - 1 month ago 13
C++ Question

Moving a member function from base class to derived class breaks the program for no obvious reason

I have this piece of code that outputs

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;
}


However, when I move the function
foo()
from
B
to
D
it starts outputting
1
(proof is on Coliru).

Questions:


  1. Why does this happen?

  2. What should the title of this question be, after it is answered?


Answer

This is why you should never using namespace std;

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

That bitset isn't what you think it is. Because B<T> is a dependent base class, members are hidden from unqualified lookup. So to access bitset, you need to access it through this, or explicitly qualify it (see here for more details):

this->bitset
B<T>::bitset

Because bitset doesn't name B<T>::bitset in the derived case, what could it mean? Well, because you wrote using namespace std;, it's actually std::bitset, and the rest of your expression just so happens to be valid. Here's what happens:

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

The 32 && 63 evaluates to true, which is promoted to 1u for the std::bitset template argument. This std::bitset is initialized with ~n & 255, and is checked for equality with oldmode. This last step is valid because std::bitset has a non-explicit constructor which allows a temporary std::bitset<1> to be constructed from oldmode.