在C ++ 11中,protected表示public? [英] In C++11, protected means public?

查看:272
本文介绍了在C ++ 11中,protected表示public?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

继续学习在 C ++错误:基本功能受到保护 ...



C ++ 11指针到成员规则有效地剥离了任何值的 protected 关键字,因为受保护的成员可以在不相关的类中访问

  class Encapsulator 
{
protected:
int i;
public:
Encapsulator(int v):i(v){}
};

封装器f(int x){return x + 2; }

#include< iostream>
int main(void)
{
Encapsulator e = f(7);
// forbidden:std :: cout<< e.i < std :: endl;因为我是protected
// forbidden:int Encapsulator :: * pi =& Encapsulator :: i;因为我是protected
// forbidden:struct Gimme:Encapsulator {static int read(Encapsulator& o){return o.i; }};

// loophole:
struct Gimme:Encapsulator {static int Encapsulator :: * it(){return& Gimme :: i; }};
int Encapsulator :: * pi = Gimme :: it();
std :: cout<< e。* pi < std :: endl;
}





这是真的符合行为根据标准?



(我认为这是一个缺陷,声明& Gimme :: i 真的应该是 int Gimme :: * 即使 i 是基类的成员, )






我实现了一些人们可能会惊讶的是第三个评论的方法(第二个意见测试案例)实际上失败了。这是因为正确的方式来思考protected不是我的派生类有访问,没有人,但如果你从我得到,你将有权访问这些继承的变量包含在你的实例中,除非你授予。例如,如果 Button 继承 Control ,则受保护的成员 Control 按钮实例中只能访问控制按钮,并且(假设 Button 不禁止它)实例的动态类型和任何中间基础。



这个漏洞颠覆了合同,完全反对规则11.4p1的精神:


当非静态数据成员或非静态成员函数是其命名类的受保护成员时,应用在条款11中较早的。
如前所述,授予对受保护成员的访问权,因为引用发生在某个类 C 的朋友或成员中。如果访问是形成指向成员的指针(5.3.1),则 nested-name-specifier 应表示 C C 。所有其他访问涉及(可能隐式)对象表达式。在这种情况下,对象表达式的类应为 C 或从 C strong>。







感谢AndreyT链接 http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed .html#203 ,它提供了激发更改的其他示例,并要求Evolution工作组提出该问题。






也相关: GotW 76:使用和滥用权限

解决方案

我已经看到这种技术,我指的是protected hack,在这里和其他地方提到了很多次。是的,这种行为是正确的,它确实是一个合法的方式来绕过受保护的访问,而不诉诸任何脏的黑客。



m 是类 Base 的成员,那么使& Derived :: m 表达式产生 Derived :: * 类型的指针是类成员指针是逆变,而不是协变。这将使得结果指针不能用 Base 对象。例如,此代码编译

  struct Base {int m; }; 
struct Derived:Base {};

int main(){
int Base :: * p =& Derived :: m; // < - 1
Base b;
b。* p = 42; //< - 2
}

因为& Derived :: m 生成 int Base :: * 值。如果它产生一个 int Derived :: * 值,代码将无法在第1行编译。如果我们试图修复它

  int Derived :: * p =& Derived :: m; //<  -  1 

它将无法在第2行编译。编译将执行强制转换

  b。* static_cast (p)= 42; // < -  2 

这不太好。



PS我同意,这不是一个非常有说服力的例子(只是使用& Base:m 从一开始,问题解决)。但是, http://www.open-std.org/jtc1/sc22/ wg21 / docs / cwg_closed.html#203 有更多的信息,说明为什么这样的决定是最初做的一些光。他们


从04/00会议备注:



当前的治疗是允许最宽的
可能使用给定的会址地址表达式。由于
a指针到基本成员可以隐式转换为
指针到派生成员,使表达式的类型a
指针到基本成员允许结果来初始化或将
分配给指针到基本成员或指针到派生成员。
接受此提案只允许后者使用。



Continuing something learned in C++ error: base function is protected ...

The C++11 pointer-to-member rules effectively strip the protected keyword of any value, because protected members can be accessed in unrelated classes without any evil/unsafe casts.

To wit:

class Encapsulator
{
  protected:
    int i;
  public:
    Encapsulator(int v) : i(v) {}
};

Encapsulator f(int x) { return x + 2; }

#include <iostream>
int main(void)
{
    Encapsulator e = f(7);
    // forbidden: std::cout << e.i << std::endl; because i is protected
    // forbidden: int Encapsulator::*pi = &Encapsulator::i; because i is protected
    // forbidden: struct Gimme : Encapsulator { static int read(Encapsulator& o) { return o.i; } };

    // loophole:
    struct Gimme : Encapsulator { static int Encapsulator::* it() { return &Gimme::i; } };
    int Encapsulator::*pi = Gimme::it();
    std::cout << e.*pi << std::endl;
}

Is this really conformant behavior according to the Standard?

(I consider this a defect, and claim the type of &Gimme::i really should be int Gimme::* even though i is a member of the base class. But I don't see anything in the Standard that makes it so, and there's a very specific example showing this.)


I realize some people may be surprised that the third commented approach (second ideone test case) actually fails. That's because the correct way to think about protected is not "my derived classes have access and no one else" but "if you derive from me, you will have access to these inherited variables contained in your instances, and no one else will unless you grant it". For example, if Button inherits Control, then protected members of Control within a Button instance are accessible only to Control, and Button, and (assuming Button doesn't prohibit it) the actual dynamic type of the instance and any intervening bases.

This loophole subverts that contract, and completely opposed the spirit of the rule 11.4p1:

An additional access check beyond those described earlier in Clause 11 is applied when a non-static data member or non-static member function is a protected member of its naming class. As described earlier, access to a protected member is granted because the reference occurs in a friend or member of some class C. If the access is to form a pointer to member (5.3.1), the nested-name-specifier shall denote C or a class derived from C. All other accesses involve a (possibly implicit) object expression. In this case, the class of the object expression shall be C or a class derived from C.


Thanks to AndreyT for linking http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#203 which provides additional examples motivating a change, and calls for the issue to be brought up by the Evolution Working Group.


Also relevant: GotW 76: Uses and Abuses of Access Rights

解决方案

I have seen this technique, that I refer to as "protected hack", mentioned quite a few times here and elsewhere. Yes, this behavior is correct and it is indeed a legal way to circumvent protected access without resorting to any "dirty" hacks.

When m is member of class Base, then the problem with making the &Derived::m expression to produce a pointer of Derived::* type is that class member pointers are contravariant, not covariant. It would make the resultant pointers unusable with Base objects. For example, this code compiles

struct Base { int m; };
struct Derived : Base {};

int main() {
  int Base::*p = &Derived::m; // <- 1
  Base b;
  b.*p = 42;                  // <- 2
}

because &Derived::m produces an int Base::* value. If it produced a int Derived::* value, the code would fail to compile at line 1. And if we attempted to fix it with

  int Derived::*p = &Derived::m; // <- 1

it would fail to compile at line 2. The only way to make it compile would be to perform a forceful cast

  b.*static_cast<int Base::*>(p) = 42; // <- 2

which is not good.

P.S. I agree, this is not a very convincing example ("just use &Base:m from the beginning and the problem is solved"). However, http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#203 has more info that sheds some light on why such decision was made originally. They state

Notes from 04/00 meeting:

The rationale for the current treatment is to permit the widest possible use to be made of a given address-of-member expression. Since a pointer-to-base-member can be implicitly converted to a pointer-to-derived-member, making the type of the expression a pointer-to-base-member allows the result to initialize or be assigned to either a pointer-to-base-member or a pointer-to-derived-member. Accepting this proposal would allow only the latter use.

这篇关于在C ++ 11中,protected表示public?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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