Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Short answer: There is no undefined behavior involved. The behavior you see is:</p> <ul> <li>The expression <code>&amp;A::a</code> is an attempt to obtain a pointer to member pointing to member a of class A. If a is protected in A, this expression only passes access checks within class A (or friends of A). In a class B derived from A, you can get the same pointer to member only via the expression <code>&amp;B::a</code> (note that the type of this expression will still be <code>int A::*</code>). So: <ul> <li>if <code>A::a</code> is protected in A, the expression <code>&amp;A::a</code> is not allowed in a member function of derived class B. This is your compiler error.</li> <li>if <code>A::a</code> is public in A, this expression is valid, producing a pointer to memeber. </li> </ul></li> <li>Streaming a pointer to member to an <code>ostream</code>, for example using <code>cout &lt;&lt; &amp;A::a</code> will print <code>1</code>. This results from invoking <code>ostream::operator &lt;&lt; (bool)</code>. You can use the boolalpha manipulator to see that this is indeed the chosen overload: <code>cout &lt;&lt; boolalpha &lt;&lt; &amp;A::a</code> will print <code>true</code>.</li> <li>If you use the modified expression &amp;(A::a) or simply &amp;a, no pointer to member is formed. Here the address of member a of the current object (i.e the same as <code>&amp;(this-&gt;a)</code>) is taken, which is a regular pointer to int. This access to a protected member of a base class subobject of <code>*this</code> is valid, so that variant can be used even if a is protected in A.</li> </ul> <p>Longer explanation:</p> <p>The standard says (5.3.1/3):</p> <blockquote> <p>The result of the unary &amp; operator is a pointer to its operand. The operand shall be an lvalue or a qualified- id. If the operand is a qualified-id naming a non-static member m of some class C with type T, the result has type “pointer to member of class C of type T” and is a prvalue designating C::m. [...]</p> </blockquote> <p>So the expression <code>&amp;A::a</code> attempts to obtain a pointer-to-member to member a of class A.</p> <p>In the next paragraph (5.3.1/4), it is elaborated that only the &amp;X::m syntax produces a pointer to member - neither <code>&amp;(X::m)</code>, nor <code>&amp;m</code> or plain <code>X::m</code> do:</p> <blockquote> <p>A pointer to member is only formed when an explicit &amp; is used and its operand is a qualified-id not enclosed in parentheses.</p> </blockquote> <p>But such an expression is only valid, if access is allowed. In case of a protected member (11.4/1) applies:</p> <blockquote> <p>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 (11.2) 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. [...]</p> </blockquote> <p>In your case access to the protected member a would be granted, because the reference to a occurs in a member of class B, derived from A. As the expression attempts to form a pointer to member, the <em>nested-name-specifier</em> (the part before the final "::a") must denote B. Thus the simplest allowed form is <code>&amp;B::a</code>. The form <code>&amp;A::a</code>is only allowed within members or friends of class A itself.</p> <p>There is no formatted output operator for pointers to member (neither as istream member nor as free operator function), so the compiler will look at overloads that can be called using a standard conversion (sequence). The only standard conversion from pointers to member to something else is described in 4.12/1:</p> <blockquote> <p>A prvalue of [...] pointer to member type can be converted to a prvalue of type bool. A [...] null member pointer value is converted to false; any other value is converted to true. [...]</p> </blockquote> <p>This conversion can be used without additional conversions to call <code>basic_ostream&lt;charT,traits&gt;&amp; basic_ostream&lt;charT,traits&gt;::operator&lt;&lt;(bool n)</code>. Other overloads require longer conversion sequences, so that overload is the best match.</p> <p>As <code>&amp;A::a</code> takes the address of some member, it is not a null member pointer value. Thus it will convert to <code>true</code>, which prints as "1" (noboolalpha) or "true" (boolalpha). </p> <p>Finally, the expression <code>&amp;(A::a)</code> is valid in a member of B, even if a is protected in A. by the above rules this expression does not form a pointer to member, so the special access rule quoted above does not apply. For such cases 11.4/1 continues:</p> <blockquote> <p>All other accesses involve a (possibly implicit) object expression (5.2.5). In this case, the class of the object expression shall be C or a class derived from C.</p> </blockquote> <p>Here the object impression is an implicit <code>(*this)</code>, i.e. <code>A::a</code> means the same as <code>(*this).A::a</code>. The type of <code>(*this)</code> obviously is the same as the class where the access occurs (B), so the access is allowed. [Note: <code>int x = A(42).a</code> would not be allowed within B.]</p> <p>So <code>&amp;(A::a)</code> within <code>B::show()</code> means the same as <code>&amp;(this-&gt;a)</code> and that is a plain pointer to int.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload