Pau*_*cas 7 c++ private base-class private-inheritance name-lookup
使用g ++ 4.2.1编译此代码:
struct S { };
template<typename T> struct ST { };
template<typename BaseType>
class ref_count : private BaseType { };
template<typename RefCountType>
class rep_base : public RefCountType { };
class wrap_rep : public rep_base<ref_count<S> > {
typedef rep_base<ref_count<S> > base_type; // line 11
};
Run Code Online (Sandbox Code Playgroud)
我明白了:
bug.cpp:1: error: ‘struct S’ is inaccessible
bug.cpp:11: error: within this context
Run Code Online (Sandbox Code Playgroud)
但是,如果我更改wrap_rep要使用的类ST:
class wrap_rep : public rep_base<ref_count< ST<int> > > {
typedef rep_base<ref_count< ST<int> > > base_type;
};
Run Code Online (Sandbox Code Playgroud)
它编译得很好.或者,如果我将原始代码更改为:
class wrap_rep : public rep_base<ref_count<S> > {
typedef rep_base<ref_count< ::S > > base_type; // now using ::
};
Run Code Online (Sandbox Code Playgroud)
它也编译好.对我来说,原始代码看起来很好.这是一个g ++错误吗?如果没有,那么为什么使用模板工作呢?而且,对于另一种情况,为什么::S必要?
这两个代码都是无效的(只有最后一个有效),但是你的编译器(不符合)只能诊断一个.正如另一个答案所说,这使用了注入的类名.类S被认为具有S表示同一类的成员名称.例如(注意S::S第一个示例中的"class"关键字是强制引用注入的类名而不是默认构造函数所必需的):
class S { };
class S::S object; // creates an S object
class X : S::S::S::S { }; // derives from class S
Run Code Online (Sandbox Code Playgroud)
类模板还具有注入的类名.与注入的类名一样,它继承到派生类,因此它ST<int>是不正确的,因为它使用了注入的类名,但是无法访问它.如果你使用GCC减去4.5,它可能与GCC4.5 引入的更改有关:
G ++现在实现DR 176.以前G ++不支持使用模板基类的inject-class-name作为类型名称,并且查找名称在封闭范围内找到模板的声明.现在查找名称会找到inject-class-name,它可以用作类型或模板,具体取决于名称后面是否有模板参数列表.由于此更改,以前接受的某些代码可能格式不正确,因为
- 注入类名不可访问,因为它来自私有基础,或
- inject-class-name不能用作模板模板参数的参数.
在这两种情况中,可以通过添加嵌套名称说明符来明确命名模板来修复代码.第一个可以使用-fno-access-control解决; 第二个只是被拒绝了.
为了让注入的类名更有趣 - 请注意注入的类名不等于首先想到的typedef.注入的类名是类名,但不归类为typedef-name,这意味着它可以被函数,对象或枚举器名称隐藏:
// valid, the data-member hides the injected class name
struct S { int S; };
Run Code Online (Sandbox Code Playgroud)
要引用注入的类名,你可以说class S::S(同样,在基类列表中,非类型名称被忽略,因此你不需要特别的预警),但是简单的查找S::S将引用数据 -会员.