3

I have a piece of code that compiles using icc or visual c++ but does not when I use gcc or clang.

The problem comes from the fact that gcc/clang want bindTo(std::string& s, const int& i) to be defined before A<T>::bind(T& t) or callBindTo(T& t) definition and not just before code instantiation.

My question is: Why Visual and icc does not require it ? and which compiler have the right behavior with respect to the standard ?

#include <string>

template <typename T>
class A
{
  public:
  static void bind(T& t);
};

template <typename T>
void A<T>::bind(T& t)
{
  std::string s;
  bindTo(s, t);
}

template <typename T>
void callBindTo(T& t)
{
  std::string s;
  bindTo(s, t);
}

void bindTo(std::string& s, const int& i)
{
  s = i;
}

int main()
{
  int i;
  A<int>::bind(i);
  callBindTo(i);
}
Vivien
  • 31
  • 2
  • This is called forward declaration. A function declaration / prototype should always precede its usage. Typically all function are declared in the header files which are included in the source file. – unbesiegbar Nov 24 '15 at 09:18
  • 1
    The term to search for is *two-phase lookup*. – T.C. Nov 24 '15 at 09:26
  • 1
    Thanks for the missing pointer ;) http://stackoverflow.com/questions/6273176/what-exactly-is-broken-with-microsoft-visual-cs-two-phase-template-instanti – Vivien Nov 24 '15 at 09:33

1 Answers1

2

GCC and Clang have the correct implementation. As the comments hint, the name t is looked up during instantiation, in the second name lookup phase, when T==int. This doesn't introduce any additional namespaces for Argument-Dependent Lookup. The compiler therefore has to look for all declarations of bind, in the global namespace, up to the point where it's actually used.

The last part is important. The scope for lookups is determined by the location of the template definition, not by the location of the first instantiation. This is a good thing. What if there were 5 instantiations, with 5 increasingly big overload sets? You'd have to instantiate the template each time, redoing overload resolution, and potentially getting 5 different results.

If you don't think that's bad enough, how would you deal with this in name mangling? You'd have callBindTo<int> and callBindTo<int>, two instantiations that could only be distinguished by the overload set of bind at their point of instantiation.

No, the current rules are sane. Yes, the Argument Dependent Lookup can add extra namespaces, and that's tricky, but all instantiations of callBindTo<std::string> add the same namespace std. And with that example, we see it's a good thing you called your function bindTo and not bind - you almost dragged in std::bind.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • You still have the "different overload set" problem with current rules, though it does significantly mitigate it. [temp.point]/8 and [temp.dep.candidate]/1 make doing this kind of thing ill-formed NDR (or UB, which is essentially equivalent). – T.C. Nov 24 '15 at 20:27