2

I don't understand the need for the static_cast in the following C++ code snippet (tested with GCC-4.7):

#include <cstdio>

class Interface
{
public:
    virtual void g(class C* intf) = 0;

    virtual ~Interface() {}
};

class C
{
public:
    void f(int& value)
    {
        printf("%d\n", value);
    }

    void f(Interface* i)
    {
        i->g(this);
    }

    template <typename T>
    void f(T& t);
    //void f(class Implementation& i);
};

class Implementation : public Interface
{
public:
    Implementation(int value_) : value(value_) {}
    void g(C* intf)
    {
        intf->f(value);
    }

private:
    int value;
};

int main()
{
    C a;
    Implementation* b = new Implementation(1);

    //a.f(b); // This won't work: undefined reference to `void C::f<Implementation*>(Implementation*&)'
    a.f(static_cast<Interface*>(b));

    delete b;

    return 0;
}

If I omit the static_cast, I get a linker error because it wants to use:

template <typename T>
void f(T& t);

instead of:

void f(Interface* i);

On the other hand, if I replace the templated method with the following (commented out in the above snippet):

void f(class Implementation& i);

then I don't get errors and I can see that the "correct" method is called at run-time (that is:

void f(Interface* i);

).

Why does the declaration of the template method affect name lookup? Many thanks in advance,

  • 2
    It seems like you're asking why a perfect match is preferred over an imperfect match that requires a conversion to a base class pointer. The answer is pretty obvious -- perfect matches are better. – David Schwartz Jan 29 '15 at 11:57

1 Answers1

4

When performing overload resolution for a.f(b), the compiler notes two facts:

First, You are trying to call f with an lvalue of type Implementation*.

Second, three functions are in the overload set: C::f(int&) and C::f(Interface*) and C::f<Implementation*>(Implementation*&). Note that the template is included since its template parameter could be deduced from the argument it is being called with.

Now the compiler starts to check which function fits "best":

  • C::f(int&) cannot be called with this argument at all.
  • C::f(Interface*) can be called, but requires one standard conversion (pointer to derived -> pointer to base)
  • C::f<Implementation*>(Implementation*&) can be called without any conversions

Thus, the template quite simply fits best. However, as you did not define an implementation for the template, the linker later on bugs out with the error message that it cannot find the function you are trying to call.

danielschemmel
  • 10,885
  • 1
  • 36
  • 58