Cod*_*cks 5 c++ inheritance templates access-modifiers this
我想了解访问修饰符关于继承的4种不同行为,当涉及使用和/或省略templates和this关键字的4种组合时.以下所有代码均在g ++ 4.8中完成:
这里有一个GrandChild类,它privateLY从继承Parent,这privateLY从继承GrandParent,其中有一个public enum n.非对象,客户端代码可以访问GrandParent::n,因为后者是一个public enum.但是GrandParent::n从内部无法进入GrandChild:
#include <iostream>
using namespace std;
struct GrandParent { enum {n = 0}; };
struct Parent : private GrandParent { enum {n = 1}; };
struct GrandChild : private Parent {
enum {n = 2};
void f() {cout << GrandParent::n << endl;}
// ^ error: 'struct GrandParent GrandParent::GrandParent'
// is inaccessible
};
int main() {
cout << GrandParent::n << endl;
// ^ non-object access would have outputted `0` had `GrandChild`'s
// definition compiled or been commented out.
}
Run Code Online (Sandbox Code Playgroud)
1.)由于拥有一个隐藏非对象访问权限的基础子对象而导致GrandParent::n内部不可访问,并且其2代代理使得基础子对象也无法访问?我期望错误信息是关于那个.GrandChildGrandChildGrandParentGrandParent::numprivaten
2.)但显然,事实并非如此.为什么错误会抱怨GrandParent构造函数?
3)前面加上this->以GrandParent::n中f()的定义会增加我的预期误差在1号,但不会删除构造函数投诉.为什么?我认为,包括this->是多余的,它的不作为将导致查找,以试图找到n对的GrandParent子对象内GrandChild的,立即范围的扣除非对象之前的范围n反正.
4.)为什么这个模板变体编译?它看起来在功能上与非模板类似:
#include <iostream>
using namespace std;
template <unsigned int N>
struct bar : private bar<N - 1> {
enum {num = N};
void g() {
static_assert(N >= 2, "range error");
cout << bar<N - 2>::num << endl;
}
};
template <>
struct bar<0> { enum {num = 0}; };
int main() {
bar<2> b2;
b2.g(); // Output: 0
}
Run Code Online (Sandbox Code Playgroud)
5.)this->在bar<N - 2>::numin g()的定义中添加前导致我在#1中预期的编译器错误.但为什么不包括#2的错误?为什么它的遗漏不会产生#2的错误?
这里的整个问题是名称查找(我认为您之前的问题之一也是这种情况)。我将尝试说明我对正在发生的事情的理解:
\n\n每个(命名的)类都有一个注入类名。例如:
\n\nstruct GrandParent\n{\n // using GrandParent = ::GrandParent;\n enum {n = 0};\n};\nRun Code Online (Sandbox Code Playgroud)\n\n您可以使用这个注入类名来引用类本身。GrandParent它对于普通类(其中非限定查找无论如何都可以在周围范围中找到名称)没有那么有用,但对于派生类和类模板来说:
namespace A\n{\n struct Foo\n {\n // using Foo = ::A::Foo;\n };\n};\n\nstruct Bar : A::Foo\n{\n void woof(Foo); // using the injected-class-name `::A::Foo::Foo`\n};\n\ntemplate<class T, int N, bool b>\nstruct my_template\n{\n // using my_template = ::my_template<T, N, b>;\n void meow(my_template); // using the injected-class-name\n};\nRun Code Online (Sandbox Code Playgroud)\n\n这不是“它是基类子对象的一部分”中的继承,而是指定非限定查找的方式:如果在当前类范围内找不到该名称,则基类范围将被搜查。
\n\n现在,对于OP中的第一个(非模板)示例:
\n\nstruct Parent : private GrandParent\n{\n // using Parent = ::Parent;\n\n enum {n = 1}; // hides GrandParent::n\n};\n\nstruct GrandChild : private Parent {\n // using GrandChild = ::GrandChild;\n\n enum {n = 2};\n void f() {cout << GrandParent::n << endl;}\n // ^ error: \'struct GrandParent GrandParent::GrandParent\'\n // is inaccessible\n};\nRun Code Online (Sandbox Code Playgroud)\n\n此处,表达式GrandParent::n调用 name 的非限定名称查找GrandParent。由于当找到名称时,非限定查找会停止(并且不考虑周围范围),因此它将找到注入的类名称GrandParent::GrandParent。也就是说,lookup 搜索GrandChild(未找到名称)的范围,然后搜索(未找到名称)的范围,最后搜索(找到注入类名称的位置)Parent的范围。GrandParent这是在访问检查之前完成的并且独立于访问检查。
GrandParent找到名称后,最终检查可访问性。名称查找需要从Parent到GrandParent查找名称。Parent由于继承是私有的,因此除 的成员和朋友之外的任何人都无法访问此路径。(你可以通过这条路径看到,但你可能不会使用它;可见性和可访问性是正交的概念。)
这是标准的 [basic.lookup.unqual]/8:
\n\n\n\n\n对于类的成员
\n\nX,成员函数体 [...] 中使用的名称应通过以下方式之一声明:\n
\n- 在使用它的块或封闭块中使用之前,或者
\n- 应该是类的成员
\nX或者是基类的成员X,或者- if是类[...]
\nX的嵌套类Y- [...]
\n- if是 命名空间或 [...]
\nX的成员,在使用名称之前,\n 在命名空间中或在包含命名空间的 \xe2\x80\x99之一中。NNN
基类中的名称查找相当复杂,因为可能必须考虑多个基类。对于单继承和在成员函数体范围内查找的成员,它从该函数所属的类开始,然后向上遍历基类(base、base of base、base of base of base、 ..)。请参阅[class.member.lookup]
\n\n模板大小写不同,bar类模板的名称也不同:
template <unsigned int N>\nstruct bar : private bar<N - 1> {\n enum {num = N};\n void g() {\n static_assert(N >= 2, "range error");\n cout << bar<N - 2>::num << endl;\n }\n};\nRun Code Online (Sandbox Code Playgroud)\n\n这里,bar<N - 2>使用的是。它是一个从属名称,就像N模板参数一样。因此,名称查找被推迟到 的实例化点g。bar<0>即使它是在函数之后声明的,也可以找到特化。
的注入类名称bar可以用作模板名称(指类模板)或类型名称(指当前实例化)[temp.local]/1:
\n\n\n与普通(非模板)类一样,类模板有一个注入类名(第 9 条)。注入的类名可以用作模板名或类型名。当它与template-argument-list一起使用时,\n 作为模板template-parameter的模板参数,或者作为友元类模板声明的详尽类型-\n 说明符中的最终标识符,它指的是类模板本身。否则,它相当于模板名称,后跟中包含的类模板的模板参数。
\n<>
也就是说,bar<N - 2>查找当前bar类(实例化)的注入类名。由于它与template-argument-list一起使用,因此它指的是 的另一个不相关的专业化。基类的注入类名是隐藏的。bar
bar<0>::num不是通过私有继承的访问路径来访问,而是直接通过当前类的注入类名来访问,引用类模板本身。num成为 的公共成员bar<0>是可以的。
| 归档时间: |
|
| 查看次数: |
415 次 |
| 最近记录: |