« 最终幻想XIII | 返回首页 | 招聘程序员 »

古怪的 C++ 问题

我好多年没写 C++ 程序了,读 C++ 代码也是偶尔为之。

今天晚上就碰到这么一个诡异的问题,我觉得是我太久没摸 C++ 了,对那些奇怪的语法细则已经不那么熟悉了。有知道的同学给我解惑一下吧。

事情的起因是,我想安装一个 perl 模块唤作 Syntax::Highlight::Universal 。

本来用 CPAN 安装很方便的,直接 install 即可。

可是在我的机器上,make 死活通不过。我就仔细研究了一下编译出错信息。又读了一下源代码,自己感觉没错。纠结了半天,仔细模仿出错的地方写了一小段程序测试。

template class A { protected: int a; }; template class B : public A { public: void foo() { a=0; } };

各位同学觉得有问题么?我初看觉得没有。但是 gcc 一编译就出错。一开始是觉得 gcc 版本太高(4.x),可能语法检查更严格了。后来换了 gcc3 ,问题依旧。

出错信息如下:

a.cpp: In member function `void B::foo()':

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这个成员

好嘛。。这个C++,哎。。。为什么用了这么久,看到这个例子还这么纠结?

这个是二段式名字查找导致的吧。。VC6是不支持二段式名字查找的,于是没问题。。

xuzhongxing正解。见clang的blog:http://blog.llvm.org/2009/12/dreaded-two-phase-name-lookup.html

Effective C++,item43

这事情在effective c++的条款43条中有提到, 因为基类A有被特化的可能, 所以成员a不一定存在, 因此必须显示指出this->a或者A<T>::a

因为后期随时可能加入这么一段代码
template<>
class A<int>{
};

g++确实对语法验证比较严格,而VC就弱一些,例如VC里编程几乎不需要写typename这个keyword,但g++就经常需要.

我觉得代码应该改成
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方法,虽然可以解决问题,但我不倾向,我觉得用作用域来解决思路更清晰一些。

template <class T>
class A {
public:
int a;
};

template <>
class A<int> {
};

这个太不符合人类直觉了。编译器或者标准应该改过来。

写了多年的C++,从来没遇到过此类问题。对于过分古怪或者复杂的template用法向来是敬而远之。

gcc和vc6对模板支持不好。

这个是典型的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 的。

@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.

模板参数依赖问题。
云风已经太久不写c++了。

符合标准的写法应该是
<code>
A<T>::a = 0;
</code>

GCC编译器不能理解继承自模板类里的数据成员,因为它认为既是模板,就是对象未明。Borland的编译器\Solaris上的Sun Studio的C++编译器\VC编译器无此问题。

是阿,C++的成员变量有时和全局变量一样难控制

不是C++标准,看这个
http://gcc.gnu.org/onlinedocs/gcc-4.4.3/gcc/Name-lookup.html#Name-lookup

Post a comment

非这个主题相关的留言请到:留言本