3

I am observing behavior in the below code which I cannot readily explain and would like to understand the theory of better. I cannot seem to find an online documentation source or existing question which covers this particular situation. For reference, I am using Visual Studio C++ 2010 to compile and run the following code:

#include <iostream>
using namespace std;

struct Bottom_Class
{
    template<typename This_Type>
    void Dispatch()
    {
        // A: When this comment is removed, the program does not compile
        //    citing an ambiguous call to Print_Hello
        // ((This_Type*)this)->Print_Hello();

        // B: When this comment is removed instead, the program compiles and
        //    generates the following output:
        //    >> "Goodbye from Top Class!"
        // ((This_Type*)this)->Print_Goodbye<void>();
    }

    void Print_Hello() {cout << "Hello from Bottom Class!" << endl;}

    template<typename This_Type>
    void Print_Goodbye() {cout << "Goodbye from Bottom Class!" << endl;}
};

struct Top_Class
{
    void Print_Hello() {cout << "Hello from Top Class!" << endl;}

    template<typename This_Type>
    void Print_Goodbye() {cout << "Goodbye from Top Class!" << endl;}
};

template<typename Top_Type,typename Bottom_Type>
struct Merged_Class : public Top_Type, public Bottom_Type {};

typedef Merged_Class<Top_Class,Bottom_Class> My_Merged_Class;

void main()
{
    My_Merged_Class my_merged_object;

    my_merged_object.Dispatch<My_Merged_Class>();
}

Why does this work differently for the templated member function vs. non-templated member function cases ?

How does the compiler decide (in the templated case) that Top_Class::Print_Goodbye() is the appropriate overload rather than Bottom_Class::Print_Goodbye() ?

Thank you in advance for your consideration.

curiousguy
  • 8,038
  • 2
  • 40
  • 58
excogitus
  • 33
  • 2

2 Answers2

3

Both comments (AFAIK correctly) generate compilation error with GCC 4.6.3. May be the Microsoft compiler is doing something incorrect.

➜  scratch  g++ -O2 templ.cc
templ.cc: In member function ‘void Bottom_Class::Dispatch() [with This_Type = Merged_Class<Top_Class, Bottom_Class>]’:
templ.cc:42:48:   instantiated from here
templ.cc:16:9: error: request for member ‘Print_Goodbye’ is ambiguous
templ.cc:22:10: error: candidates are: template<class This_Type> void Bottom_Class::Print_Goodbye()
templ.cc:30:10: error:                 template<class This_Type> void Top_Class::Print_Goodbye()
parry
  • 504
  • 3
  • 9
  • This is not a proper answer, instead it should have been a comment. – Some programmer dude Jul 28 '12 at 00:27
  • 3
    How so? OP asked why one works and other doesn't. Ideally both should not work - that's the correct behavior. So the answer might be that it's a compiler issue as demonstrated. No? – parry Jul 28 '12 at 00:29
  • You are merely confirming that it works/not works. You don't give an answer to _why_ there might be a problem. – Some programmer dude Jul 28 '12 at 00:33
  • 3
    OP asked why does the compiler find one to be ambiguous and not the other. That implies that he/she understands that compiler isn't able to derive which method to call. But what he/she finds is that in one case the compiler is able to choose a method to call. The correct behavior is for the compiler to reject both - which I demonstrated using GCC. I fail to understand how this doesn't answer OPs question - I even hinted that it might have to do with Microsoft's compiler behaving incorrectly! – parry Jul 28 '12 at 00:37
  • 1
    @JoachimPileborg "_You don't give an answer to why there might be a problem._" Why there is a problem is crystal clear: there is an **inherent ambiguity** here: two (template) member functions with exactly the same signature; the two classes are (or look?) **symmetric**. Unless parry and the OP missed something subtle that allows the compiler to break the symmetry between both classes, it is a correct answer. Even if parry missed something and is wrong here, it is a well-formed incorrect answer: it answers the question, but incorrectly. – curiousguy Aug 04 '12 at 16:24
1

In the Dispatch method, This_Type is the same as My_Merged_Class. The My_Merged_Class has two methods with the names of Print_Hello, of course the compiler is going to have problems to distinguish between them.

The call to Print_Hello in Dispatch, after template replacement, looks like this:

((My_Merged_Class*)this)->Print_Hello();

I hope the above substitution helps you see better why there is an ambiguity. The same problem should actually occur for Print_Goodbye, but it might be a bug in the compiler you are using that lets it through.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Wow, such quick responses. It seems everyone agrees that it is an MSVC-specific bug, which I suppose I get to report to Microsoft. I wish I could check both answers, because both are helpful, but I am checking this one because it was a little clearer on explaining why it is most likely a compiler bug. Out of curiosity, was anyone able to replicate my observation? – excogitus Jul 28 '12 at 01:21