策略继承和不可访问的受保护成员

时间:2013-03-29 11:15:15

标签: c++ inheritance visibility protected policy

似乎模板策略类中的受保护成员是不可访问的,即使类层次结构似乎是正确的。

例如,使用以下代码段:

#include <iostream>
using namespace std;

template <class T>
class A {
  protected:
    T value;
    T getValue() { return value; }
  public:
    A(T value) { this->value = value; }
};

template <class T, template <class U> class A>
class B : protected A<T> {
  public:
    B() : A<T>(0) { /* Fake value */ }
    void print(A<T>& input) {
      cout << input.getValue() << endl;
    }
};

int main(int argc, char *argv[]) {
  B<int, A> b;
  A<int> a(42);
  b.print(a);
}

编译器(OS X上的clang,但gcc返回相同类型的错误)返回以下错误:

Untitled.cpp:18:21: error: 'getValue' is a protected member of 'A<int>'
      cout << input.getValue() << endl;
                ^
Untitled.cpp:25:5: note: in instantiation of member function 'B<int, A>::print' requested here
  b.print(a);
    ^
Untitled.cpp:8:7: note: can only access this member on an object of type 'B<int, A>'
    T getValue() { return value; }
      ^
1 error generated.

奇怪的是,编译器的最后一个注释完全正确但已经应用,因为b对象的类型为'B<int, A>'。这是编译器错误还是代码中存在问题?

由于

4 个答案:

答案 0 :(得分:9)

您误解了受保护访问的含义。

受保护的成员可以通过派生类调用。但仅限于类本身内包含的基础对象。

例如,如果我简化问题,请使用:

class A {
protected:
    void getValue(){}
};

class B : protected A
{
public:
    void print(A& input)
    {
        input.getValue(); //Invallid
    }
};

除了类本身内的“A”对象之外,不能在“A”对象上调用getValue。 例如,这是有效的。

    void print()
    {
        getValue(); //Valid, calling the base class getValue()
    }

Dan Nissenbaum和shakurov指出。但这也是有效的:

void print(B& input)
{
    input.getValue();
}

这是因为我们明确地说输入是B的对象。编译器知道B的所有对象都保护了对getValue的访问。在我们传递A&amp;的情况下,该对象可能也是一种C类型,可以通过私人访问来自A。

答案 1 :(得分:6)

让我们忘记关于模板的一分钟,看看这个:

 class A {
   protected:
     int value;
     int getValue() { return value; }
   public:
     A(int value) { this->value = value; }
 };

 class B : protected A {
   public:
     B() : A(0) { /* Fake value */ }
     void print(A& input) {
       cout << input.getValue() << endl;
     }
 };

print()方法的实施是错误的,因为您无法访问A内的B的非公开成员。这就是为什么:来自{ {1}},您只能访问B的非公开成员。这些成员可能是继承的,也可能不是 - 这没关系。

另一方面,B可能不是A& input实例的引用。它可能是对另一个子类的引用(可能B无法访问)。

答案 2 :(得分:2)

不要被模板分心。它与错误无关。编译器抱怨的main中的行创建了B<int, a>类型的对象,并尝试访问受保护的成员。无论哪种类型,这都是不合法的。您只能使用内部成员函数或朋友函数中的受保护成员。例如:

struct S {
protected:
    void f();
};

int main() {
    S s;
    s.f(); // error: attempts to call a protected member function
}

答案 3 :(得分:2)

派生类的成员函数可以访问其类型的任何对象中受保护的基类成员,只要作为参数传递的对象的显式声明类是参数传递的参数。派生类(或进一步派生类)。

作为 base 类类型显式传递的对象不能在派生类的成员函数中访问其受保护成员。

换句话说,如果我们有:

class A
{
protected:
    int x;
}

class B : public A
{
    void foo(B b)
    {
        b.x; // allowed because 'b' is explicitly declared as an object of class B
    }
    void goo(A a)
    {
        a.x; // error because 'a' is explicitly declared as having *base* class type
    }
};

...然后不允许行a.x,因为参数的显式类型是A,但受保护访问的规则仅适用于明确定义为相同<的对象/ strong>类作为尝试访问该成员的类。 (...或者从它派生的类;即,如果class C派生自B,则传递显式声明为类C的对象的对象也将具有x } B成员函数中可访问的。)

当他写作(释义)时,shakurov给出了这个原因

  

A和输入可能不是对B实例的引用。它可能是一个   引用另一个子类(可能有getValue())   不可访问的)

这里给出了一个很好的解释:accessing a protected member of a base class in another subclass

感兴趣的是,我认为这来自C ++标准:

  

11.4受保护的成员访问[class.protected] 1当a时,应用超出前面第11章所述的附加访问权限检查。   非静态数据成员或非静态成员函数是受保护的   其命名类的成员(11.2)115如前所述,访问a   授予受保护的成员,因为引用发生在朋友中   或某些类C的成员。如果访问是形成指针   member(5.3.1),嵌套名称说明符应表示C或类   从C派生。所有其他访问涉及(可能是隐含的)   对象表达式(5.2.5)。在这种情况下,对象的类   表达式应为C或源自C的类。