Reputation: 394
The following is a simplification of an iterator I'm using on an std::tuple
. In it, I define a templated type in one namespace, and use it in a second one, such as follows:
namespace meta{
template<size_t> struct meta_size_t {};
}
namespace ns{
//template<size_t> struct ns_size_t {};
template <size_t N>
void bar(meta::meta_size_t<N>) {
bar(meta::meta_size_t<N-1>());
}
void bar(meta::meta_size_t<1>) {}
template<size_t N>
void foo() {
bar(meta::meta_size_t<N>());
}
}
int main(void){
ns::foo<5>();
return 0;
}
The code compiles fine in MSVC2015, but fails in g++ 4.8 and clang 3.5 (-std=c++11) by reaching the maximum recursion depth for templates. Note that if meta::meta_size_t
is replaced with ns_size_t
(and the definition of ns_size_t
is uncommented), then everything works fine.
I surmise that the compiler is delaying the resolution of meta::meta_size_t
until it's done resolving bar
since it's in another namespace (or something along these lines), and is thus failing, but I'm unsure about how to tackle this issue.
Under what conditions does this problem generally arise? Is there any way of forcing the compiler to resolve namespace meta's
contents before ns's
? I'd like to avoid duplicating the type definition (as exemplified with ns_size_t
).
Further Context: In the original code, bar
has a std::tuple
parameter, and makes a call to an overloaded function using std::get<N>(tuple)
Edit: Noobish mistake. After looking at the example provided by @WhozCraig, I verified that the code could be fixed by swapping the order of the two bar
methods (I presume that g++ and clang search for definitions sequentially, and thus never get to registering the second overload of bar, whereas MSVC must add all the definitions to its symbol table before continuing). Does the standard specify one approach or the other, or is this implementation specific? That said, I don't get why this isn't a problem if the definitions aren't inside a namespace.
Upvotes: 3
Views: 356
Reputation: 137310
The basic rule is that when you write foo(/* something dependent on a template parameter*/);
, ordinary unqualified lookup for foo
considers the template definition context only, while ADL will consider both the definition and the instantiation contexts. As a result, the only way for an overload that isn't found in the template definition context to be considered is by ADL.
At bar(meta::meta_size_t<N-1>());
, void bar(meta::meta_size_t<1>) {}
is not in scope, so the only way it could be called is by argument-dependent lookup, but ns
is not an associated namespace of meta::meta_size_t<1>
, so instead you get infinite recursion.
When you use ns_size_t
, then ns
is an associated namespace, so the second overload of bar
is found by ADL and selected by overload resolution, terminating the recursion.
When you swap the order of the bar
s, then ordinary unqualified lookup will find the terminating case, so all is well as well.
MSVC is well-known for its nonconformance in the area of template name lookup.
Upvotes: 5