错误的私人基类无法访问? [英] Erroneous private base class inaccessible?

查看:323
本文介绍了错误的私人基类无法访问?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用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
};

我得到:

  bug.cpp:1:错误:'struct S'不可访问
bug.cpp:11:错误:在此上下文中
wrap_rep 类使用 ST

  class wrap_rep:public rep_base< ref_count< ST< int> > > {
typedef rep_base< ref_count< ST< int> > > base_type;
};

它编译正常。或者,如果我将原始代码更改为:

  class wrap_rep:public rep_base< ref_count< S& > {
typedef rep_base< ref_count< :: S> > base_type; // now using ::
};

它也编译正常。对我来说,原来的代码似乎很好。这是一个g ++错误吗?如果不是,那为什么使用模板工作?对于其他情况,为什么 :: S 必需?

解决方案

这两个代码都是无效的(只有最后一个是有效的),但是你的编译器(不符合)只诊断一个。另一个答案说,这使用注入的类名。类 S 被认为具有表示同一类的成员名 S 。例如(注意在第一个例子中 S :: S 之前的class关键字是强制引用注入的类名而不是默认构造函数的必要条件):

  class S {}; 

class S :: S object; //创建一个S对象
class X:S :: S :: S :: S {}; //派生自类S

类模板也有一个注入的类名。与注入的类名一样,它继承到派生类,因此 ST 是错误的,因为它使用注入的类名,但不可访问。如果您使用GCC少于4.5,则可能与使用GCC4.5的更改介绍有关:


G ++现在实现了DR 176.以前的G ++不支持使用模板基类的inject-class-name作为类型名,并查找名称,在包围范围中找到模板的声明。现在查找名称将找到inject-class-name,它可以用作类型或模板,具体取决于名称是否后跟模板参数列表。由于此更改,以前接受的某些代码可能会出现错误,因为


  1. inject-class-因为它来自私有基础,或

  2. inject-class-name不能用作模板模板参数的参数。

在这两种情况下,代码都可以通过添加一个嵌套名称说明符来明确命名模板。第一个可以使用-fno访问控制;第二个只有-pedantic拒绝。







注入的类名更有趣 - 注意注入的类name不等同于一个可能先认为的typedef。注入的类名是类名,但不被分类为typedef名,这意味着它可以被函数,对象或枚举器名称隐藏:

  // valid,data-member隐藏注入的类名
struct S {int S; };

要引用注入的类名,你可以说 class S :: S (同样,在基类列表中,非类型名称被忽略,因此你不需要特别的预警),但是一个简单的查找 S :: S 将引用数据成员。


Compiling this code using 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
};

I get:

bug.cpp:1: error: ‘struct S’ is inaccessible
bug.cpp:11: error: within this context

However, if I change the wrap_rep class to use ST:

class wrap_rep : public rep_base<ref_count< ST<int> > > {
  typedef rep_base<ref_count< ST<int> > > base_type;
};

it compiles fine. Alternatively, if I change the original code to:

class wrap_rep : public rep_base<ref_count<S> > {
  typedef rep_base<ref_count< ::S > > base_type;  // now using ::
};

it also compiles fine. To me, the original code seems fine as-is. Is this a g++ bug? If not, then why does using a template work? And, for the other case, why is the ::S necessary?

解决方案

Both of those codes are invalid (only the last one is valid), but your compiler (which is not conforming) only diagnoses one. As another answer says, this uses the injected class name. A class S is considered to have a member name S denoting that same class. For example (notice the "class" keyword before S::S in the first example is necessary to force a reference to the injected class name, instead of the default constructor):

class S { };

class S::S object; // creates an S object
class X : S::S::S::S { }; // derives from class S

Class templates also have an injected class name. Like the injected class name, it is inherited to derived classes, and thus ST<int> is ill-formed because it uses that injected class name, which however is not accessible. If you use GCC less 4.5, it may have something to do with a change introduced with GCC4.5:

G++ now implements DR 176. Previously G++ did not support using the injected-class-name of a template base class as a type name, and lookup of the name found the declaration of the template in the enclosing scope. Now lookup of the name finds the injected-class-name, which can be used either as a type or as a template, depending on whether or not the name is followed by a template argument list. As a result of this change, some code that was previously accepted may be ill-formed because

  1. The injected-class-name is not accessible because it's from a private base, or
  2. The injected-class-name cannot be used as an argument for a template template parameter.

In either of these cases, the code can be fixed by adding a nested-name-specifier to explicitly name the template. The first can be worked around with -fno-access-control; the second is only rejected with -pedantic.


To have a bit more fun with injected class names - notice that the injected class name is not equivalent to a typedef as one might think first. The injected class name is a class-name, but is not classified as a typedef-name, which means it can be hidden by function, object or enumerator names:

// valid, the data-member hides the injected class name
struct S { int S; };

To refer to the injected class name you can say class S::S (likewise, in a base-class list, non-type names are ignored thus you don't need special pre-cautions there), but a simple lookup to S::S will refer to the data-member.

这篇关于错误的私人基类无法访问?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆