【问题标题】:Strange compile error regarding overload resolution关于重载解析的奇怪编译错误
【发布时间】:2011-04-27 01:28:26
【问题描述】:

这段代码片段:

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 之前,或者 lastns 之外声明,则它会编译。

对此有何解释?

【问题讨论】:

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


    【解决方案1】:

    给定

    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&lt;first&gt; 与第二个f 的参数类型不匹配。

    这会导致递归调用 f( arg 类型为 chain&lt;last&gt; )。当尝试为参数类型chain&lt;last&gt; 实例化f 时,会出现错误,因为chain&lt;last&gt; 中没有next 属性。

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

    干杯,

    【讨论】:

      【解决方案2】:

      案例一)

      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&lt;last&gt;&amp;) // #2 上方指定template&lt;&gt; 来确保#2#1 的模板特化

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

      在函数模板上方添加重载声明将使您的代码编译。

      解决方案:

      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的POI就在main之后,其中#2是可见的。
      • 我的困惑(也许是 uj2 的)是 main 中对 f 的调用是选择模板化的 f 而不是非模板化的 f。在main 的通话中,这两个功能都是可见的,对吧?为什么会这样?
      • @JoshD:关于考虑哪些重载有复杂的规则。我的猜测是,非正式地说,如果编译器可以在定义时决定调用什么,那么它不会在实例化时改变该决定。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-22
      • 2016-01-23
      相关资源
      最近更新 更多