2

I am very confused about the standard 10.2/13,

[ Note: Even if the result of name lookup is unambiguous, use of a name found in multiple subobjects might still be ambiguous (4.11, 5.2.5, 5.3.1, 11.2).—end note ] [ Example:

struct B1 {
  void f();
  static void f(int);
  int i;
};
struct B2 {
  void f(double);
};
struct I1: B1 { };
struct I2: B1 { };
struct D: I1, I2, B2 {
  using B1::f;
  using B2::f;
  void g() {
    f(); // Ambiguous conversion of this
    f(0); // Unambiguous (static)
    f(0.0); // Unambiguous (only one B2)
    int B1::* mpB1 = &D::i; // Unambiguous
    int D::* mpD = &D::i; // Ambiguous conversion
  }
};

I cannot see why this is unambiguous int B1::* mpB1 = &D::i; // Unambiguous

Visual C++, Gcc and CLang all say that it is ambiguous access to D::i!

The wording seems to be related to the core issue #39 http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#39, and the final proposal is here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1626.pdf

I now find that the new algorithm-based wordings (10.2/3-10.2/6) are even more confusing because none of the note in 10.2/9, 10.2/10, 10.2/11, and 10.2/13 fully complies to 10.2/3-10.2/6. I can take 10.2/9-10.2/11 as exceptions, but I am especially confused about 10.2/13. I have no idea on the intention of 10.2/13.

How should the example in 10.2/13 be looked-up according to the 10.2/3-10.2/6? What's the intention of 10.2/13, i.e., what's the situation that 10.2/13 is considered as an exception of 10.2/3-10.2/6?

Please give me some hints. Thank you very much.


After some thinking, I think the intention of 10.2/13 is clearer to me.

int B1::* mpB1 = &D::i; // Unambiguous

This should be unambiguous and the current compilers are wrong on this. This is unambiguous because pointer to class members initialization doesn't involve in accessing the object yet.

int D::* mpD = &D::i; // Ambiguous conversion

This actually means when convert from int B1::*mpB1 to int D::*mpD, the conversion is ambiguous due to ambiguous base classes.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
user534498
  • 3,926
  • 5
  • 27
  • 52
  • see also "The Dreaded Diamond": http://www.parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.8 – Doug T. Jan 20 '11 at 02:47

4 Answers4

4

For the B1::* case, the interpretation is unambiguous, simply being the offset from the start of B1 to i.

In 5.3.1/3:

struct A { int i; };
struct B : A { };
... &B::i ... // has type int A::*

So the trick is to make &D::i be of type B1::* in the first place. Then:

int B1::* mpB1 = &D::i; // Unambiguous

is simple. The interest then comes in:

int D::* mpD = &D::i; // Ambiguous conversion

Here the RHS is of type B1::* and needs a conversion as we need to determine which base is being referred to.

Keith
  • 6,756
  • 19
  • 23
  • +1 for the most important bit --from which I learned: `&D::i` is of type `int B1::*`, not `int D::*`. The quote from the standard would be in §4.11/2. Being picky, the conversion is not ambiguous due to a diamond, as no diamond is present in the example: there are two base `B1` subobjects in `D`. If there was a diamond, the conversion would not be ambigous (there would be a single `B1` subobject of `D`) Still, I think it would still be ill-formed according to §4.11/2. – David Rodríguez - dribeas Jan 20 '11 at 09:15
2

This:

int B1::* mpB1 = &D::i; // Unambiguous

Is unambiguous because the result is being assigned to a pointer to member of class B.
So it don't matter which i is chosen as the offset is relative to a B member (not the parent D class).

So it is unambiguous to you and me but I don't think the compiler can handle it.

Doug T.
  • 64,223
  • 27
  • 138
  • 202
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • I did upvote this answer, but now I feel that it is missing the important bit present in @Keith answer: the result of `&D::i` is of type `int B1::*`, and it is an offset from a `B1` base. The ambiguity is in the conversion required by the assignment. The quote from the standard would be in §4.11/2. – David Rodríguez - dribeas Jan 20 '11 at 09:11
  • "_So it don't matter which i is chosen as the offset is relative to a B member_" Sorry, that doesn't make sense. – curiousguy Dec 23 '11 at 04:56
  • @curiousguy: The only `i` is within `B1`. Since we are getting the address of a member we are getting the offset of the member within the class. Thus in the unambiguous situation there is only one value to choose. The distance of i from the base of B1. In the second instance we are assigning the result as an offset of a D pointer so there are actually four different results for offsets of i within D. – Martin York Dec 23 '11 at 05:05
  • @LokiAstari "_Since we are getting the address of a member we are getting the offset of the member within the class._" That is only an implementation detail. The issue is name lookup: which member `i` in `D`? "_In the second instance we are assigning the result as an offset of a D pointer so there are actually four different results for offsets of i within D._" Why four? – curiousguy Dec 23 '11 at 05:26
  • @curiousguy: By 4 I mean "more than 1" – Martin York Dec 23 '11 at 05:52
0

A quick check on ISO IEC 14882 2003 Section 10 does not have this example, or anything similar to this. C++0x is draft standard, and VC++/GCC/CLang are not compliant to it.

My Guess: This is some byproduct of the new auto typing and not found in older C++ standard.

J-16 SDiZ
  • 26,473
  • 4
  • 65
  • 84
0

FWIW, I'm copying over my answer I gave to the usenet copy of this question:

Hi all,
 
I am very confused about the standard n3225 10.2/13,  
[ Note: Even if the result of name lookup is unambiguous, use of a name found in multiple subobjects might still be ambiguous (4.11, 5.2.5, 5.3.1, 11.2).—end note ] [ Example:  

struct B1 {
  void f();
  static void f(int);
  int i;
};
struct B2 {
  void f(double);
};
struct I1: B1 { };
struct I2: B1 { };
struct D: I1, I2, B2 {
  using B1::f;
  using B2::f;
  void g() {
    f(); // Ambiguous conversion of this
    f(0); // Unambiguous (static)
    f(0.0); // Unambiguous (only one B2)
    int B1::* mpB1 = &D::i; // Unambiguous
    int D::* mpD = &D::i; // Ambiguous conversion
  }
};

I cannot see why this is unambiguous int B1::* mpB1 = &D::i; // Unambiguous  

&D::i has type int B1::*, and unambiguously refers to data member i of  B1. If you dereference it with a D object or if you assign it to a int  D::*, you will get an ambiguity as needed. 

Visual C++, Gcc and CLang all say that it is ambiguous access to D::i!  

None of those compilers implement 10.2 yet. 

The wording seems to be related to the core issue #39 http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#39, and the final proposal is here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1626.pdf   I now find that the new algorithm-based wordings (10.2/3-10.2/6) are even more confusing because none of the note in 10.2/9, 10.2/10, 10.2/11, and 10.2/13 fully complies with 10.2/3-10.2/6. I can take 10.2/9-10.2/11 as exceptions, but I am especially confused about 10.2/13. I have no idea on the intention of 10.2/13.  

You need to give examples that show what you don't understand. 

How should the example in 10.2/13 be looked-up according to the 10.2/3-10.2/6? What's the intention of 10.2/13, i.e., what's the situation of which 10.2/13 is considered as an exception of 10.2/3-10.2/6?  

The new algorithm based lookup rules decouples runtime concerns (finding an  unique object) from compile time/lookup concerns (finding a declaration that  a name refers to). 

The following is well-formed with the new wording:

struct Z { int z; };
struct X : Z { };
struct Y : Z { };
struct A : X, Y { };

struct B : A {
  using A::z;
};

The declaration using A::x; introduces a member name into B that refers to  the declaration Z::z. In a declarative context, this is perfectly fine. An  error is risen only when you access B::z as a member access expression  (5.2.5). 

Don't feel bad about having mistaken this about the member pointer case. I did so too in the past, and the corresponding issue report actually made it into a C++0x draft. They luckily changed it back when they noticed that change was wrong.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212