Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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()? } 
like image 286
Leon Avatar asked Nov 07 '16 10:11

Leon


People also ask

Which member function in the base class is used to define in a derived class?

Explanation: The member function which is defined in base class and again in the derived class, is overridden by the definition given in the derived class. This is because the preference is given more to the local members. When derived class object calls that function, definition from the derived class is used.

What will happen when a derived class object invokes a function which is in the base class also?

Now if we call this function using the object of the derived class, the function of the derived class is executed. This is known as function overriding in C++. The function in derived class overrides the function in base class.

What happens if the base and derived class?

[D]. Base class object will call base class function and derived class object will call derived class function.

What can be inherited by a derived class from a base class?

The Base class members and member functions are inherited to Object of the derived class. A base class is also called parent class or superclass. Derived Class: A class that is created from an existing class. The derived class inherits all members and member functions of a base class.


1 Answers

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.

like image 155
TartanLlama Avatar answered Sep 21 '22 02:09

TartanLlama