Sharing the Heritage (Quiz Game)

Sharing the Heritage (Quiz Game)

  • Comments 31

I was reading recent discussion threads and found something interesting that someone introduced as a question, but instead of start developing the whole story from here, I’ll just challenge you with two different quiz games.

Quiz 1
  1. #include <iostream>
  2.  
  3. class Foo {
  4. public:
  5.     virtual void DoStuff()=0;
  6. };
  7.  
  8. class Bar : public Foo {
  9. public:
  10.     virtual void DoStuff(int a)=0;
  11. };
  12.  
  13. class Baz : public Bar {
  14. public:
  15.     void DoStuff(int a) override
  16.     {
  17.         std::cout << "Baz::DoStuff(int)";
  18.     }
  19.  
  20.     void DoStuff() override
  21.     {
  22.         std::cout << "Baz::DoStuff()";
  23.     }
  24. };
  25.  
  26. int main() {
  27.     Baz baz;
  28.     Bar *pBar = &baz;
  29.  
  30.     pBar->DoStuff();
  31. }

Without using Visual Studio or your building tool of preference, just your knowledge, if we tried to run this code, what is expected to see in the console? Why?

Now let’s make a little variation and substitute the method getting no arguments for one getting char, as follows:

Quiz 2
  1. #include <iostream>
  2.  
  3. class Foo {
  4. public:
  5.     virtual void DoStuff(char a)=0;
  6. };
  7.  
  8. class Bar : public Foo {
  9. public:
  10.     virtual void DoStuff(int a)=0;
  11. };
  12.  
  13. class Baz : public Bar {
  14. public:
  15.     void DoStuff(int a) override
  16.     {
  17.         std::cout << "Baz::DoStuff(int)";
  18.     }
  19.  
  20.     void DoStuff(char a) override
  21.     {
  22.         std::cout << "Baz::DoStuff(char)";
  23.     }
  24. };
  25.  
  26. int main() {
  27.     Baz baz;
  28.     Bar *pBar = &baz;
  29.  
  30.     pBar->DoStuff('a');
  31. }

If we ran this new version, provided that it compiles with no errors, what is expected to be seen in the output console? Why?

 

Solutions are explained in this sequel.

  • I agree with Ben Craig.  The first won't compile because DoStuff() is hidden in Bar. The second one will return an int because a char can be converted to an int...and there is nothing with the signature void DoStuff(char a) to override, so the compile will use the signature void DoStuff(int a).

  • It seems that everyone except Istval has forgotten that 'a' is an INTEGER literal not a char literal in C++.  Now admittedly that may not relevant due to the hiding rules in C++.  

    One question that I do have though is how the hiding rules interact with abstract and virtual methods.

  • [Ben]

    > The standard does not define 0 to mean success.

    Yes, it does. CornedBee already cited the relevant paragraphs in C++0x. In C++03 they're 3.6.1 [basic.start.main]/5 and 18.3 [lib.support.start.term]/8. In C99 they're 5.1.2.2.3/1 and 7.20.4.3.

    [SimonRev]

    > It seems that everyone except Istval has forgotten that 'a' is an INTEGER literal not a char literal in C++.

    This is incorrect. C++03 2.13.2 [lex.ccon]/1: "A character literal is one or more characters enclosed in single quotes, as in 'x', optionally preceded by the letter L, as in L'x'. A character literal that does not begin with L is an ordinary character literal, also referred to as a narrow-character literal. An ordinary character literal that contains a single c-char has type char, with value equal to the numerical value of the encoding of the c-char in the execution character set. An ordinary character literal that contains more than one c-char is a multicharacter literal. A multicharacter literal has type int and implementation-defined value."

    (This is a difference between C and C++. C99 6.4.4.4/10: "An integer character constant has type int." By the way, I cite C99 because it's freely available, I don't have C89, and I'm familiar with the differences between C89 and C99 - I'm well aware that VC doesn't support the C99 Core Language.)

  • Stephan T. Lavavej wrote:

    > This is a difference between C and C++.

    There was an interesting explanation in the D&E:

    "As first defined, the C++ overloading rules accepted the limitations of C's built-in types. That is, there were no values of type float (technically, no rvalues) because in computation a float is immediately widened to a double. Similarily there were no values of type char because in every use a char is widened to an int. [...] Consider an output function. If we can't overload based on the char/int distinction, we have to use two names. [...] To overcome this, the type rules of C++ were changed to allow types such as char and float to be considered in their unpromoted form by the overload resolution mechanism. In addition, the type of a literal character, such as 'X', was defined to be char." (D&E, 11.2.1)

    This was not a problem with C because printf() relies on format specifiers while C++ relies on types.

    And also, I'm not sure it's a good idea to replace newlines with paragraph tags in a programming blog.

  • My first post was for Quiz 1.

    For Quiz 2.

    Reading the C++ Standard Document n3092, I found section 13.3.3 Best viable function [over.match.best]. In the part 2 it states.

    “If there is exactly one viable function that is a better function than all other viable functions, then it is the

    one selected by overload resolution; otherwise the call is ill-formed.”

    This means that DoStuff(‘a’) will be compared to all visible methods that would work. In this case a char can be cast to an int without loss so it uses the void DoStuff(int a) as it can’t see void DoStuff(char a) per the reason of Quiz 1.

  • It appears my first post did not take. I will repost for Quiz 1.

    Reading the C++ Standard Document n3092, I found section 13.2 Declaration matching [over.dcl]. In part 1 it states

    “A function member of a derived class is not in the same scope as a function member of the same name in a base class.”

    This means that DoStuff() in Foo can’t be overloaded by DoStuff(int a) in Bar as Foo is not in scope. If you add virtual void DoStuff()=0; to Bar it will work.

  • This is a pretty neat exercise as it took quite a bit of reading of the standard to find the cause. This reading exposed a lot of other things when looking at the examples in the document. As you know it is not the most fun document to read cover to cover. :) Doing targeted research on it as a reference makes it more fun.

    Thank You.

  • One question I have related to Quiz 1 is that the Standard Document Section 13.2 Declaration matching part 1 in the example says the following.

    -“struct B {

    -     int f(int);

    -};

    -struct D : B {

    -     int f(char*);

    -};

    -Here D::f(char*) hides B::f(int) rather than overloading it.

    -void h(D* pd) {

    -   pd->f(1);         // error:

    -                         // D::f(char*) hides B::f(int)

    -   pd->B::f(1);      // OK   <----------------------------------This is the example.

    -   pd->f("Ben");   // OK, calls D::f

    -}”

    I should be able to do the following in the last line of code in Quiz 1.

    pBar->Foo::DoStuff();

    Visual Studio 2010 did not like that.

  • @MikeK , Did you give implementation for Foo::DoStuff() ? If yes, it should compile and Foo::DoStuff() is called.

  • According C++ rules,

    Quiz 1's output is:     Baz::DoStuff();

    Quiz 2's output is:     Baz::DoStuff(char);

    According one company's COXX rules, perhaps we will seen

    Quiz 1:     Baz::DoStuff(int)

    Quiz 2:     Baz::DoStuff(int)

    Is it right?

  • Quiz1 : Code doesn't compile.

    Quiz2: Baz::DoStuff(int)

  • I want black ops games installed on my computer but I do not know. Please someone help me.

  • I agree with Ben Craig.  

    For first one, compile should complain that there is no 'DoStuff (void)' due to C++ name hiding.

    For the second one, with same logic on name hiding, compile will implicitly convert the char into int and call DoStuff(int).

  • Both may give output "Baz::DoStuff(int)"

  • CLI/C++ != C++

Page 2 of 3 (31 items) 123