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

This (made-up) question was initially formulated as a puzzle, concealing some of the details that might help seeing the problem faster. Scroll down for the simpler MCVE version.


Original (a-la puzzle) version

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).

Why does this happen?


MCVE version

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

Solution 1:

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 this1, 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.


1 Note that we need to parenthesise this->bitset in this case due to some pretty subtle parsing disambiguity rules. See Template dependent base member is not resolved properly for details.

Solution 2:

Yes, because bitset will be interpreted as non-dependent name, and there is a template named std::bitset<T>, hence, it will be parsed as:

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

You need to make it like this:

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

or better, don't use using namespace std;

Solution 3:

  1. Why does this happen?

For the derived class, B<T> is not a nondependent base class, it can't be determined without knowing the template argument. And bitset is a nondependent name, which won't be looked up in dependent base class. Instead, std::bitset is used here (because of using namespace std;). So you'll get:

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

You could make the name bitset dependent; because dependent names can be looked up only at the time of instantiation, and at that time the exact base specialization that must be explored will be known. For example:

return this->bitset < 32                   // The mouth is not full of teeth
//     ~~~~~~
       && 63 > (~n & 255) == oldmode       // Fooness holds
       && regex_match(s, sig_regex);       // Signature matches

or

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

or

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. What should the title of this question be, after it is answered?

"How to access the nondependent names in template base class?"

Solution 4:

This is a really cool example!!! :)

I think - what is happening is this:

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

Is parses as construct me a bitset