Strange compile error regarding overload resolution
This code fragment:
namespace ns
{
struct last;
struct first
{
typedef last next;
};
template <typename T>
struct chain
{
chain<typename T::next> next;
};
template <>
struct chain<last>
{
};
}
using namespace ns;
template <typename T>
void f(const T& x) // #1
{
f(x.next);
}
void f(const chain<last>&) // #2
{
}
int main()
{
f(chain<first>());
}
gives the following error on Comeau, and a very similiar error on GCC:
"ComeauTest.c", line 27: error: class "ns::chain<ns::last>" has no member "next"
f(x.next);
^
detected during:
instantiation of "void f(const T &) [with T=ns::chain<ns::last>]"
at line 27
instantiation of "void f(const T开发者_StackOverflow社区 &) [with T=ns::chain<ns::first>]"
at line 36
It does compile, however, if either #2
is defined ahead of #1
, or if last
is declared outside of ns
.
Any explanation for this?
Case 1)
template <typename T>
void f(const T& x) // #1
{
f(x.next); //where's f ??
}
void f(const chain<last>&) // #2
{
}
You need to make sure that #2
is a template specialization of #1
by specifying template<>
above void f(const chain<last>&) // #2
Without template<>
void f(const chain<last>&)
would be interpreted as an overload of f
. So a call to f(x.next);
would be ill formed because of the missing declaration of void f(const chain<last>&)
.
Adding a declaration of the overload above the function template would make your code compile.
Solutions:
1)
template <typename T>
void f(const T& x) // #1
{
f(x.next); //hmm specialized version down there.
}
template<>
void f(const chain<last>&) // #2
{
}
2)
void f(const chain<last>&); // #0
template <typename T>
void f(const T& x) // #1
{
f(x.next); //hmm I can see #0, call #2
}
void f(const chain<last>&) // #2
{
}
Case 2)
void f(const chain<last>&) // #2
{
}
template <typename T>
void f(const T& x) // #1
{
f(x.next); // found!!
}
Given
template <typename T>
void f(const T& x) // #1
{
f(x.next);
}
void f(const chain<last>&) // #2
{
}
... the call to f
in the body of the first can not ever call the second f
, because the second f
is not visible at that point.
So if you get into the first f
, then it will recurse down to first error. :-) I'm talking about compile time recursion here, as long as next
is of a type that f
is not yet instantiated for.
And the call in main
, ...
int main()
{
f(chain<first>());
}
... necessarily calls the first f
, since chain<first>
does not match the argument type of the second f
.
This results in recursive call f(
arg of type chain<last>
)
. And when trying to instantiate f
for argument type chain<last>
you get an error, since there's no next
attribute in chain<last>
.
Regarding the code apparently compiling OK when placing the declaration of last
in the global namespace, I don't know. Are you sure? Note: I haven't tried any of this with a real compiler.
Cheers & hth.,
精彩评论