开发者

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.,

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜