20

Consider this code:

#include <iostream>

namespace N {
    class A {};
    void f(A a) { std::cout << "N::f\n"; }
}

void f(int i) { std::cout << "::f\n"; }

template <typename T>
class Base {
  public:
    void f(T x) { std::cout << "Base::f\n"; }
};


template <typename T>
class X : public Base<T> {
  public:
    void g() {
        T t;
        f(t);
    }
};

int main()
{
    X<N::A> x1;
    x1.g();

    X<int> x2;
    x2.g();
}

The code is intended to investigate how name lookup works in C++.

If I compile this program with GNU C++ (version 6.1.0), it prints:

N::f
::f

But if I compile it with Microsoft Visual Studio 2015, it prints:

Base::f
Base::f

Which is the correct behaviour, and why?

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
oz1cz
  • 5,504
  • 6
  • 38
  • 58
  • 6
    I don't know the answer to this question per say, but I do know that Microsoft Visual Studio's C++ compiler is often not standard complying. (So my guess is that Microsoft VS is wrong) – DarthRubik Aug 30 '16 at 12:54
  • 3
    VS 2015 should be getting really close to being standard though. Clang seems to behave like GCC after a quick check. So without knowing the details I would also assume VS is wrong. Maybe you stumbled over a bug here. – Hayt Aug 30 '16 at 12:58

2 Answers2

15

g++ is Standard compliant here, and Visual C++ not:

14.6.2 Dependent names [temp.dep]

3 In the definition of a class or class template, the scope of a dependent base class (14.6.2.1) 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.

Replacing f() with this->f() will find the base member.

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • Thank you, TemplateRex. I wonder why C++ has this rule. Is it because template specialization may yield a `Base` class with no `f` function? – oz1cz Aug 30 '16 at 14:38
  • 3
    @oz1cz exactly! There is a whole passage (9.4.2.) on this in the reference book C++ Templates the Complete Guide. – TemplateRex Aug 30 '16 at 14:42
6

In the function definition of the function g the name f is considered as a function declared outside the class (within the class definition this name is not declared; f is a dependent name).

template <typename T>
class X : public Base<T> {
  public:
    void g() {
        T t;
        f(t);
    }
};

So the compiler uses the ADL lookup.

However if to write an explicit call of a member function

class X : public Base<T> {
  public:
    void g() {
        T t;
        this->f(t);
    }
};

then the call of the function f will be considered as a call of the member function of the base class..

Thus it seems that MS VC++ 2015 has a bug.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 1
    Why is `f` considered a name declared outside the scope of the class? Is that normal behavior and it only falls back into the class scope if nothing can be found outside the class ? – NathanOliver Aug 30 '16 at 13:18
  • @NathanOliver It is a specific behavior of template derived classes. I have no at hand the C++ Standard so I can not get the relevant quote. – Vlad from Moscow Aug 30 '16 at 13:20
  • That is interesting. I'll have to do some looking. g++ seems to not even fall back as [this](http://coliru.stacked-crooked.com/a/ea0c1776325985e1) fails to compile. – NathanOliver Aug 30 '16 at 13:22
  • @NathanOliver It confirms what I wrote. The name f looked outside the class definition (because in the template class definition there is no member with the name f) is not exists. – Vlad from Moscow Aug 30 '16 at 13:26
  • @NathanOliver See this question http://stackoverflow.com/questions/27178483/lookup-of-dependent-names-in-c-template-instantiation – Vlad from Moscow Aug 30 '16 at 13:30