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 this
1, 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:
- 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
- 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