有关重载决策的奇怪编译错误

时间:2010-10-18 16:59:02

标签: c++ templates namespaces compiler-errors overload-resolution

此代码片段:

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>());
}

在Comeau上给出以下错误,在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 &) [with T=ns::chain<ns::first>]"
                      at line 36

但如果在#2之前定义#1,或在last之外声明ns,则会进行编译。

对此有何解释?

2 个答案:

答案 0 :(得分:3)

案例1)

template <typename T>
void f(const T& x)          // #1
{
    f(x.next); //where's f ??
}

void f(const chain<last>&)  // #2
{
}

您需要确保#2#1的模板专精,方法是指定template<>以上的void f(const chain<last>&) // #2

没有template<> void f(const chain<last>&)会被解释为f的重载。因此,由于缺少f(x.next);的声明,对void f(const chain<last>&)的调用将会形成错误。

在函数模板上面添加重载声明会使代码编译。

解决方案:

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
{
}

案例2)

void f(const chain<last>&)  // #2
{
}

template <typename T>
void f(const T& x)          // #1
{
    f(x.next); // found!!
}

答案 1 :(得分:0)

鉴于

template <typename T>
void f(const T& x)          // #1
{
    f(x.next);
}

void f(const chain<last>&)  // #2
{
}

...对第一个f主体中f的调用无法调用第二个f,因为此时第二个f不可见。

因此,如果您进入第一个next,那么它将递归到第一个错误。 :-)我在这里谈论编译时间递归,只要f属于main尚未实例化的类型。

int main() { f(chain<first>()); } 中的来电,......

f

...必须调用第一个chain<first>,因为f与第二个f(的参数类型不匹配。

这会产生chain<last> )类型的递归调用f arg。在尝试为参数类型chain<last>实例化next时,您会收到错误,因为chain<last>中没有last属性。

关于将{{1}}的声明放在全局命名空间中时显然编译OK的代码,我不知道。你确定吗?注意:我没有尝试使用真正的编译器。

干杯&amp;第h。,