g++ 不喜欢模板 var 上的模板方法链接?

问题描述 投票:0回答:3

我正在尝试使用 g++ 编译之前在 Visual C++ 2008 Express Edition 下开发的一些代码,看起来 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()
设为普通方法也可以。使
Inner::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
c++ templates gcc g++
3个回答
12
投票

只是为了提供一些关于为什么需要

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
是成员的名称,然后编译器会看到
(
。 唯一的可能是
get_inner
是一个函数名,所以这是一个函数调用。 然后它解析参数,直到找到结束
)

3.

val . get_inner () .

对于第一步,它现在知道 get_inner 返回的值必须是类类型,因此它知道下一个标识符是成员对象或函数。

4。

val . get_inner () . get <

那么,

<
可能意味着什么? 当然,它是模板参数的开始......或者也许它是小于运算符?

我们知道

get
只能是一个对象或一个函数。 如果它是一个对象,那么
<
作为小于运算符就非常有意义。 此外,标准或多或少规定,只有在
<
之前的名称是
template-name
时,才会将
<
视为模板参数 (14.2/3):

名称查找(3.4)发现名称是模板名称后,如果该名称后跟

<
,则
<
始终被视为模板参数列表的开头,而不是名称后跟小于运算符。

在这种情况下,编译器不知道表达式

val.get_inner()
的类型是什么,因此无法查找
get
。 它或多或少地假设它是一个成员对象而不是模板名称。 '<' is treated as the less than operator and the compiler ends up checking if
get
小于
int
- 因此出现错误。

那么,为什么这些修复有效?

添加

template
关键字

从字面上看,我们告诉编译器

get
是模板名称,因此
<
运算符被视为模板参数列表的开头。

删除模板参数

当 do_outer 没有模板参数时,即:

val . get_inner () . get (
,编译器期望成员
get
是一个对象或函数。
(
消除了两者之间的歧义,并且名称被视为函数。 稍后模板参数推导即可计算出模板参数的类型。


8
投票

你可以尝试一下吗?

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

我无法访问 gcc atm,但我遇到过类似的问题,添加 template 关键字总是可以解决这些问题。而且它在 VS 中也有效。


-1
投票

我不能声称自己是地球上完全理解 C++ 模板的 10 个人之一,但你在这里所做的对我来说看起来很好。 (它在 GCC 4.4.1 上失败,并出现相同的错误,顺便说一句)。

do_outer
更改为

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

适用于 GCC,并且可能也适用于 Visual C++。

您可以考虑向 GCC 提交错误;要么他们会修复它,要么它将被关闭为无效,在此过程中有人希望解释为什么你正在做的不是有效的代码。

进一步更新和 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 接受。

© www.soinside.com 2019 - 2024. All rights reserved.