Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which compiler is correct for the following overloading/specialization behavior?

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?

like image 276
ididak Avatar asked Jan 23 '23 22:01

ididak


1 Answers

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.

like image 108
CB Bailey Avatar answered Apr 25 '23 16:04

CB Bailey