代码之家  ›  专栏  ›  技术社区  ›  Rômulo Ceccon

G++不喜欢模板变量上的模板方法链接?

  •  5
  • Rômulo Ceccon  · 技术社区  · 15 年前

    我正试图用 G+ 以前开发的一些代码 Visual C++ 2008快速版 ,而且看起来g++不允许我对模板变量的方法返回的引用调用模板方法。我可以将问题缩小到以下代码:

    class Inner
    {
    public:
      template<typename T>
      T get() const
      {
        return static_cast<T>(value_);
      };
    private:
      int value_;
    };
    
    class Outer
    {
    public:
      Inner const& get_inner() { return inner_; };
    private:
      Inner inner_;
    };
    
    template<typename T>
    int do_outer(T& val)
    {
      return val.get_inner().get<int>();
    }
    
    int main()
    {
      Outer outer;
      do_outer(outer);
      return 0;
    }
    

    代码在微软的编译器下编译得很好,但是G++抛出了一个错误:

    $ g++ -c main.cpp
    main.cpp: In function ‘int do_outer(T&)’:
    main.cpp:24: error: expected primary-expression before ‘int’
    main.cpp:24: error: expected ‘;’ before ‘int’
    main.cpp:24: error: expected unqualified-id before ‘>’ token
    

    其中第24行是指 return val.get_inner().get<int>(); .

    如果我做 do_outer 一种接收 Outer 引用编译的代码。制作 Inner::get() 正常的方法也可以。制作 内::GET() 返回void并接收模板参数也有效,因为 int 下面的说明变得不必要,即:

    class Inner
    {
    public:
      template<typename T>
      void get(T& val) const
      {
        val = static_cast<T>(value_);
      };
    private:
      int value_;
    };
    
    ...
    
    template<typename T>
    int do_outer(T& val)
    {
      int i;
      val.get_inner().get(i);
      return i;
    }
    
    ...
    

    (G++不会抱怨上面的代码。)

    现在我没主意了。怎么了?gcc/g++有问题吗?我的代码是否存在符合性问题?

    我使用的编译器是:

    $ g++ --version
    g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3
    
    3 回复  |  直到 15 年前
        1
  •  8
  •   stijn    15 年前

    你能试试吗?

    template<typename T>
    int do_outer(T& val)
    {
      return val.get_inner().template get<int>();
    }
    

    我没有访问GCCCatm的权限,但我也遇到了类似的问题,添加模板关键字总能解决这些问题。它也适用于vs。

        2
  •  11
  •   Richard Corden    15 年前

    只是为了提供一些背景 template 需要关键字:

    template<typename T>
    int do_outer(T& val)
    {
      int i;
      val.get_inner().get<int>(i);
      return i;
    }
    

    当编译器看到这个函数时,它不知道 val 是。因此,它会解析该行 val.get_inner().get(i) 如下:

    1: val .

    编译器看到 . 因此可以假定“val”具有类类型,下一个标识符是成员对象或函数的名称。

    2。 val . get_inner (

    get_inner 是成员的名称,然后编译器将看到 ( . 唯一的可能性是 盖特内 是一个函数名,所以这是一个函数调用。然后分析参数,直到找到结束符 ) .

    三。 val . get_inner () .

    至于第一步,它现在知道从get-inner返回的必须是类类型,所以它知道下一个标识符是成员对象或函数。

    4。 val . get_inner () . get <

    那么,什么可以 < 可能是什么意思?当然,这是模板参数的开始…或者可能是小于运算符?

    我们知道 get 只能是对象或函数。如果它是一个对象,那么 < 作为“小于”运算符是完全有意义的。此外,标准或多或少规定,只有在名称之前 < 是一个 template-name 它能治好 < 作为模板参数(14.2/3):

    在名称查找之后(3.4)发现名称是模板名称,如果该名称后面跟有 < , the < 始终作为模板参数列表的开头,从不作为名称后跟小于运算符。

    在这种情况下,编译器不知道表达式的类型 val.get_inner() 是的,所以它不能查找 得到 . 它或多或少地假定它是一个成员对象,而不是模板名称。<'被视为小于运算符,编译器将检查 得到 小于 int -因此出现了错误。

    那么,为什么要修复?

    添加 模板 关键字

    实际上,我们告诉编译器 得到 是模板名,因此 < 运算符被视为模板参数列表的开头。

    删除模板参数

    当Do-Outer没有模板参数时,即: val . get_inner () . get ( 编译器期望成员 得到 是一个对象或函数。这个 ( 在这两者之间消除歧义,名称被视为一个函数。然后,模板参数推导出模板参数的类型。

        3
  •  -1
  •   Jack Lloyd    15 年前

    我不能自称是地球上10个完全理解C++模板的人之一,但是你在这里做的对我来说很好。(GCC 4.4.1失败,错误相同,btw)。

    改变 do_outer

    const Inner& inner = val.get_inner();
    return inner.get<int>();
    

    与GCC一起工作,大概也将用Visual C++工作。

    您可能会考虑向gcc提交一个bug;要么他们会修复它,要么它将作为无效代码关闭,在这个过程中,希望有人能解释为什么您所做的是无效代码。

    进一步的更新和aha:结果发现它实际上不是有效的代码,gcc只是给出了一个可怕的错误消息。英特尔C++输出(实际上很有用!)错误信息:

    template.cpp(24): error: type name is not allowed
        return val.get_inner().get<int>();
    

    这让我意识到了问题所在。将Do_Inner更改为

      return val.get_inner().template get<int>();
    

    ICC和GCC都接受该规范。