Rôm*_*con 5 c++ compiler-construction gcc templates g++
我正在尝试用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;
}
Run Code Online (Sandbox Code Playgroud)
代码在Microsoft的编译器下编译得很好,但是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
Run Code Online (Sandbox Code Playgroud)
第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;
}
...
Run Code Online (Sandbox Code Playgroud)
(g ++不会抱怨上面的代码.)
现在我没有想法了.有什么问题?gcc/g ++有问题吗?我的代码是否存在合规性问题?
我正在使用的编译器是:
$ g++ --version
g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3
Run Code Online (Sandbox Code Playgroud)
Ric*_*den 11
只是为了说明为什么template
需要关键字的背景:
template<typename T>
int do_outer(T& val)
{
int i;
val.get_inner().get<int>(i);
return i;
}
Run Code Online (Sandbox Code Playgroud)
当编译器看到这个函数时,它不知道它的类型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
只能是一个对象或一个函数.如果它是一个物体,那么<
作为一个小于运算符就是完美的意义.此外,标准或多或少表明只有在<
a 之前的名称才会template-name
将它<
视为模板参数(14.2/3):
在名称查找(3.4)发现名称是模板名称后,如果此名称后跟a
<
,<
则始终将其作为模板参数列表的开头,并且永远不会作为名称后跟小于运算符.
在这种情况下,编译器不知道表达式的类型是什么val.get_inner()
,因此无法查找get
.它或多或少地假定它是成员对象而不是模板名称.'<'被视为小于运算符,编译器最终检查是否get
小于int
- 因此错误.
那么,为什么修复工作?
添加template
关键字
从字面上看,我们告诉编译器它get
是一个模板名称,因此<
操作符被视为模板参数列表的开头.
删除模板参数
当do_outer没有模板参数时,即:val . get_inner () . get (
编译器期望该成员get
是对象或函数.这(
两者之间的歧义和名称被视为一种功能.稍后模板参数推导然后计算出模板参数的类型.
你可以试试吗?
template<typename T>
int do_outer(T& val)
{
return val.get_inner().template get<int>();
}
Run Code Online (Sandbox Code Playgroud)
我无法访问gcc atm,但我遇到了类似的问题,添加模板关键字总能解决它们.它也适用于VS.