Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ template overload resolution called from template function pick candidate declared after template instantiation

Here is a minimalist example of a very strange overload resolution from a template context:

#include <iostream>

// Types //
struct I { int v; };

template <class T>
struct D { T t; };

// Functions //

// Overload 1
template <class T>
I f(T) { return {1}; }

// Template indirection that calls f(T)
template <class T>
I g(D<T>) { return f(T{}); }

// Non template indirection that calls f(T)
I h(D<I>) { return f(I{}); }

int main() {
    std::cout
        << f(I{}).v     // f(I{}) overload called directly
        << "\n"         //    => overload 1 called
        << h(D<I>{}).v  // f(I{}) called though non-template
        << "\n"         //    => overload 1 called
        << g(D<I>{}).v  // f(I{}) called though template
        << "\n";        //    => overload 2 actually called ???
}

// Overload 2
// Should not be reachable as declared after all f(...) calls.
// If was a candidate, would be chosen over 1.
I f(I) { return {2}; }

This seem to be related to ADL because if I is placed in a namespace "overload 1" is always called.

I know that ADL is performed as if the call was made from the template instantiation point (main).

For a dependent name used in a template definition, the lookup is postponed until the template arguments are known, at which time ADL examines function declarations with external linkage (until C++11) that are visible from the template definition context as well as in the template instantiation context, while non-ADL lookup only examines function declarations with external linkage (until C++11) that are visible from the template definition context. http://en.cppreference.com/w/cpp/language/unqualified_lookup#Template_definition

But here "Overload 2" is declared AFTER main! main being the instanciation point of g and f, I assumed only function declared before main would be overload candidates.

Note that this behaviour is related to g being template as h (g's equivalent not template function) calls "Overload 1".

How can "overload 2" -being declared after main- ever be called ?

This behaviour has been reproduced with clang++ (3.8.1) and g++ (6.2.1).

like image 601
Mat-Tso Avatar asked Oct 07 '16 21:10

Mat-Tso


1 Answers

[temp.dep.candidate]/1:

If the call would be ill-formed or would find a better match had the lookup within the associated namespaces considered all the function declarations with external linkage introduced in those namespaces in all translation units, not just considering those declarations found in the template definition and template instantiation contexts, then the program has undefined behavior.

Moreover, [temp.point]/6, emphasis mine:

A specialization for a function template [...] may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. [...] If two different points of instantiation give a template specialization different meanings according to the one-definition rule ([basic.def.odr]), the program is ill-formed, no diagnostic required.

The behavior of your program is undefined.

like image 102
T.C. Avatar answered Oct 30 '22 16:10

T.C.