3

这段代码:奇怪的编译错误有关重载

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

给出科莫下面的错误,并在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之外声明的。

对此有何解释?

回答

3

案例1)

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

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

您需要通过上述void f(const chain<last>&) // #2

指定template<>没有template<>void f(const chain<last>&)将被解释为f过载,确保#2#1模板专业化。因此,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!! 
} 
+0

但'#1'是'main',其中'#2'可见之后的POI。 – uj2 2010-10-18 17:18:19

+0

@ uj2:'#2'在被调用的位置不可见。 – 2010-10-18 17:23:47

+0

我的困惑(也许是uj2的)是,在main调用f选择模板化的f而不是非模板化的f。在'main'的调用中,这两个函数都是可见的,对吗?这是为什么发生? – JoshD 2010-10-18 17:37:54

0

ģ伊芬

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

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

...在第一永远不能称之为第二f的身体调用f,因为第二f是不是在这一点上是可见的。

所以,如果你进入第一个f,然后将递归下降到第一个错误。 :-)我在这里谈论编译时递归,只要nextf尚未实例化的类型。

而且在main通话,...

int main() 
{ 
    f(chain<first>()); 
} 

...必然要求第一f,因为chain<first>不匹配第二f的参数类型。

这会导致递归调用f(参数类型chain<last>)。并且当尝试为参数类型chain<last>实例化f时,由于chain<last>中没有next属性,所以出现错误。

关于将全局命名空间中的last声明放在明显编译好的代码,我不知道。你确定?注意:我没有用真正的编译器试过这些。

干杯&心连心,