古怪的 C++ 问题
我好多年没写 C++ 程序了,读 C++ 代码也是偶尔为之。
今天晚上就碰到这么一个诡异的问题,我觉得是我太久没摸 C++ 了,对那些奇怪的语法细则已经不那么熟悉了。有知道的同学给我解惑一下吧。
事情的起因是,我想安装一个 perl 模块唤作 Syntax::Highlight::Universal 。
本来用 CPAN 安装很方便的,直接 install 即可。
可是在我的机器上,make 死活通不过。我就仔细研究了一下编译出错信息。又读了一下源代码,自己感觉没错。纠结了半天,仔细模仿出错的地方写了一小段程序测试。
各位同学觉得有问题么?我初看觉得没有。但是 gcc 一编译就出错。一开始是觉得 gcc 版本太高(4.x),可能语法检查更严格了。后来换了 gcc3 ,问题依旧。
出错信息如下:
a.cpp: In member function `void B
a.cpp:11: error: `a' was not declared in this scope
就是在 B 里找不到 A 定义的成员变量 a 。
如果 A 不是一个 template ,那么这个问题就没有。
我琢磨着这个问题跟编译器为 template 生成代码的行为有关,但是不确定。
注:这段代码在 VC6 里是可以正常编译的。
最后,我试了一下,把代码改成
void foo() {
this->a=0;
}
那么是可以编译通过的了。可是,这是新标准规定的么?
btw, 其实我写 C++ 的最后一年,都养成了显式用 this 指针的习惯。这样比较少犯错误。
Comments
这个问题《Effective C++》里讲过,当模板类作为基类时会有这种情况,因为基类需要具现化之后才会有a这个成员
Posted by: 书豪 | (19) September 22, 2010 04:58 PM
好嘛。。这个C++,哎。。。为什么用了这么久,看到这个例子还这么纠结?
Posted by: 李扬 | (18) February 5, 2010 10:13 PM
这个是二段式名字查找导致的吧。。VC6是不支持二段式名字查找的,于是没问题。。
Posted by: sandy | (17) February 5, 2010 06:42 PM
xuzhongxing正解。见clang的blog:http://blog.llvm.org/2009/12/dreaded-two-phase-name-lookup.html
Posted by: roy_hu | (16) February 5, 2010 07:51 AM
Effective C++,item43
Posted by: 水的影子 | (15) January 31, 2010 11:33 AM
这事情在effective c++的条款43条中有提到, 因为基类A有被特化的可能, 所以成员a不一定存在, 因此必须显示指出this->a或者A<T>::a
因为后期随时可能加入这么一段代码
template<>
class A<int>{
};
Posted by: Scan | (14) January 30, 2010 12:44 PM
g++确实对语法验证比较严格,而VC就弱一些,例如VC里编程几乎不需要写typename这个keyword,但g++就经常需要.
Posted by: dwing | (13) January 30, 2010 12:36 PM
我觉得代码应该改成
template <class T>
class A {
protected:
int a;
};
template <class T>
class B : public A<T> {
public:
void foo() {
A<T>::a=0;
}
};
因为这是一个关于作用域的问题,我在用g++的时候经常遇到这个问题,我觉得是g++对语法要求更严格。
云风的this方法,虽然可以解决问题,但我不倾向,我觉得用作用域来解决思路更清晰一些。
Posted by: 阿土仔(垚垚) | (12) January 30, 2010 10:16 AM
template <class T>
class A {
public:
int a;
};
template <>
class A<int> {
};
Posted by: Anonymous | (11) January 29, 2010 11:07 PM
这个太不符合人类直觉了。编译器或者标准应该改过来。
Posted by: 雷勇 | (10) January 29, 2010 10:17 PM
写了多年的C++,从来没遇到过此类问题。对于过分古怪或者复杂的template用法向来是敬而远之。
Posted by: analyst | (9) January 29, 2010 11:34 AM
gcc和vc6对模板支持不好。
Posted by: kai | (8) January 29, 2010 10:38 AM
这个是典型的C++名字查找问题。C++使用two-phase name lookup.在parse模版的时候,第一遍找所有的非dependant-name.而把所有的dependant name留到实例化模版的时候查找。在这个例子中,如果仅仅写'a',那么编译器不认为这是个dependant name,所以就在第一遍parse的时候进行name lookup.但这时候是不考虑模版基类的。所以找不到这个名字。如果加上this->a,那么这就是个dependant name,所以第一遍parse的时候不管它,到实例化的时候再查找。
2-phase name lookup的第2阶段的名字解析,只是进行dependant name 的查找和argument dependant lookup。
Visual C++和老版本的GCC并没有真正的实现2-phase name lookup,而是把模版类里所有的名字查找都留到实例化的时候进行,所以才会接受那样的代码。Clang和新版的GCC是严格执行2-phase name lookup 的。
Posted by: xuzhongxing | (7) January 29, 2010 10:28 AM
@TheAnswer 应该是C++标准,在<C++ templates>9.4.2 Dependent Base Classes一节中也提到过这种dependent和nondependent的规则(见你给的链接),其中一句话说到:
Hence, the C++ standard specifies that a nondependent name appearing in a template is looked up as soon as it is encountered.
Posted by: Kevin Lynx | (6) January 29, 2010 09:05 AM
模板参数依赖问题。
云风已经太久不写c++了。
Posted by: wuqing | (5) January 29, 2010 08:38 AM
符合标准的写法应该是
<code>
A<T>::a = 0;
</code>
Posted by: Atry | (4) January 29, 2010 08:19 AM
GCC编译器不能理解继承自模板类里的数据成员,因为它认为既是模板,就是对象未明。Borland的编译器\Solaris上的Sun Studio的C++编译器\VC编译器无此问题。
Posted by: xuxl | (3) January 29, 2010 08:08 AM
是阿,C++的成员变量有时和全局变量一样难控制
Posted by: voidinit | (2) January 29, 2010 01:57 AM
不是C++标准,看这个
http://gcc.gnu.org/onlinedocs/gcc-4.4.3/gcc/Name-lookup.html#Name-lookup
Posted by: TheAnswer | (1) January 29, 2010 12:47 AM