16
#include <iostream>

void foo()
{
    std::cout << "global foo()" << std::endl;
}

struct A {
    void foo()
    {
        std::cout << "A::foo()" << std::endl;
    }
};

struct B : public A {
    void call()
    {
        foo();
    }
};

int main(int argc, char **argv )
{
    B b;
    b.call();
    return 0;
}

This gives expected result:

A::foo()

However after changing two lines (class B to template):

#include <iostream>

void foo()
{
    std::cout << "global foo()" << std::endl;
}

struct A {
    void foo()
    {
        std::cout << "A::foo()" << std::endl;
    }
};

template <typename T> // change here
struct B : public T {
    void call()
    {
        foo();
    }
};

int main(int argc, char **argv )
{
    B<A> b; // and here
    b.call();
    return 0;
}

I get unexpected result:

global foo()

And using this-> is not an option as I am trying to create a "fallback" mechanism.

elmo
  • 1,189
  • 1
  • 10
  • 35

3 Answers3

16

What you get is an expected result. This is called "Two-phase name lookup" in the C++ standard.

Names inside templates are divided into two types:

Dependent – names that depend on the template parameters but aren’t declared within the template.

Non-dependent – names that don’t depend on the template parameters, plus the name of the template itself and names declared within it.

When the compiler tries to resolve some name in the code, it first decides whether the name is dependent or not, and the resolution process stems from this distinction. While non-dependent names are resolved "normally" – when the template is defined, the resolution for dependent names happens at the point of the template’s instantiation.

foo(); in B::call in your example is a non-dependent name, so it is resolved to global foo() at the point of template definition.

Andriy
  • 8,486
  • 3
  • 27
  • 51
  • How does that work with Jagannath's comment "It works fine VS 11 Beta. Calls A::foo()."? I know MS is not the best thing to validate any standards against. Also any references to C++ (03) standard would be nice. And do you know how to make it behave as I want? – elmo Apr 20 '12 at 14:57
  • 3
    @elmo: Microsoft C++ does not implement two-phase name lookup correctly. See [this question](http://stackoverflow.com/questions/6273176). – Mike Seymour Apr 20 '12 at 14:59
  • @elmo: To make it work your way : template struct B : public T { void call() { T::foo();//Add T:: to the code } }; – Forever Learner Apr 20 '12 at 15:04
  • 3
    @elmo: The relevant section of the standard is C++11 14.6.2/3: "In the definition of a class or class template, if a base class depends on a _template-parameter_, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member." (C++03 14.6.2/3 says more-or-less the same thing, if you're interested in historicaly standards for some reason). – Mike Seymour Apr 20 '12 at 15:10
3

The accepted answer explains why you see that behaviour, but not how to acheive the "fallback" behaviour you want. That can be done using SFINAE, by introducing a pair of member template overloads, one of which will only exist if the base class has a member function called foo.

template <typename T>
struct B : T {
    template <void (T::*)()> struct has_mem_fn {};

    template <typename U> void call(has_mem_fn<&U::foo>*) {this->foo();}
    template <typename U> void call(...) {foo();}

    void call() {call<T>(0);}
};

struct X {};

int main()
{
    B<A> ba;
    ba.call();  // A::foo()

    B<X> bx;
    bx.call();  // global foo()
}

UPDATE: I've just noticed your comments in another answer, where you say you're aware of this method, but can't use it due to having to support dysfunctional compilers. In that case, I'm afraid that what you want is probably impossible.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
0

You need to specifically tell to use T class method.

template <typename T>
struct B : public T {
    void call()
    {
        T::foo();
    }
};


But as for a fallback mechanism, you can check this question: Is it possible to write a template to check for a function's existence?

Using a Substitution failure is not an error (SFINAE), you can check for a method foo in T, and then run the proper method.

Community
  • 1
  • 1
Rodrigo Ribeiro
  • 374
  • 1
  • 8
  • Please see his comment and the last line of his question. – Jagannath Apr 20 '12 at 14:54
  • Again: last line in my question states clearly it would not work: http://ideone.com/YuoIH – elmo Apr 20 '12 at 14:55
  • @elmo: why do you except it works when ``C`` structure has no ``foo`` method? http://ideone.com/WcLQE – fogbit Apr 20 '12 at 15:06
  • @Jagannath: i meant "what method/function the compiler should link in the string 33 (``c.call()``)"? ``struct C`` has no ``foo``, ``B`` has no ``foo``, what should be called in this case? – fogbit Apr 20 '12 at 15:17
  • Yeah, my bad. Didn't see that. – Rodrigo Ribeiro Apr 20 '12 at 15:21
  • Edited to, what i think, is a better answer for your case. – Rodrigo Ribeiro Apr 20 '12 at 15:41
  • As for edited version I am well aware of that approach. Unfortunately I have to create code that is compiled with CC version Sun C++ 5.9 SunOS_sparc Patch 124863-12 2009/04/21 and any SFINAE tricks I could come up with make that compiler fail miserably. If I had proper compilers I wouldn't probably reach that question. – elmo Apr 20 '12 at 15:44