views:

225

answers:

2

Consider the following code:

#include <stdio.h>

namespace Foo {
  template <typename T>
  void foo(T *, int) { puts("T"); }

  template <typename T>
  struct foo_fun {
    static void fun() { foo((T *)0, 0); };
  };
}

namespace Foo {
  void foo(int *, int) { puts("int"); }
}

using namespace Foo;

int main() {
  foo_fun<int> fun;
  fun.fun();
}

What's the expected output? "T" or int?

One compiler (gcc 4.0.1 from Apple's Xcode 3.1.2) output "int", two other compilers (gcc 4.1.2 and 4.1.3) output "T".

If I move foo(int *, int) declaration/definition before the foo(T *, int) version, all output "int". Is the order of overloading/specialization in this case defined by the current standard?

+9  A: 

The second void foo(... is an overload (and not a specialization) which is not visible at the definition of foo_fun::fun so it won't be found in the context of the template definition. Because T* is a dependent type, resolution of foo in the expression foo((T*)0, 0) will be delayed until template instantiation time and the context of the instantiation will also be considered. However, 14.6.4.2 of the standard says that if the function name is an unqualified-id but not a template-id then for non-ADL lookup only functions visible at the point of definition of the template are considered. There are no function arguments from the Foo namespace so no argument dependent lookup occurs, hence the template version of foo is called and not the non-template overload.

Many thanks to litb for the corrections to this answer.

If you made it a specialization as below, then as specializations are chosen at template instantiation time, the specialization can be called so long as the relevant specialization is visible at the point at which the function template is first instantiated for int.

namespace Foo {
    template<>
    void foo<int>(int *, int) { puts("int"); }
}

Chapter 14 of the current standard, but it's not very readable :)

Edit: If I had to pick the most relevant part of the standard it would probably be 14.6 [temp.res] para 9. (Slightly abbreviated) If a name does not depend on a template-parameter, a declaration for that name shall be in scope at the point at where the name appears in the template definition; the name is bound to the declaration found at that point and this binding is not affected by declarations that are visible at the point of instantiation.

Edit, edit: But you also need to take into account 14.6.4.2 [temp.dep.candidate]. It is very difficult and dangerous to try and reference the standard because of all the interdependencies, this answer is a case in point.

Charles Bailey
Thanks! that makes sense. So you're saying g++ 4.0.1 from Apple is not standard conforming?
ididak
I would go out on a limb and say that, yes.
Charles Bailey
I updated the code with using namespace Foo (the difference from the compilers is still there). The explanation of instantiation context doesn't sound convincing.
ididak
Updated again, we'll get there in the end!
Charles Bailey
Yeah, 14.6.4.2 is the key here.
ididak
yeah your answer is indeed correct. i found out now (which i already suspected, but were not sure) that the example in 14.6/9 is flawed (see http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#197 ). only ADL happens at the POI. deleted my answer because it was based on that flawed example
Johannes Schaub - litb
also see http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/3078f837b11a6141/85d0fb9e9e9032b8 for a useful discussion about it
Johannes Schaub - litb
and indeed, the current draft n2800 contains the fix already. yay! :)
Johannes Schaub - litb
A: 

As a general rule, of two versions of a compiler, the later is more likely to be more standard.

David Thornley
This is generally correct. However Apple's gcc includes it's own fixes, which happened to come out later (with Xcode 3.1.2) than gcc 4.1.2/3.
ididak