 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
William M. Miller Guest
|
Posted: Wed Jul 09, 2003 10:03 pm Post subject: Re: Using declarations and class member ambiguities |
|
|
""William Hayes"" <whayes (AT) hfx (DOT) andara.com> wrote
| Quote: | The Standard says that "...because a using-declaration designates a base
class member (and not a member subobject or a member function of a base
class subobject), a using-declaration cannot be used to resolve inherited
member ambiguities" (7.3.3, 14). The example it cites involves an
inheritance hierarchy containing multiple copies of a base class:
struct A { int x(); };
struct B : A { };
struct C: A {
using A: ;
int x(int);
};
struct D : B, C {
using C: ;
int x(double);
};
int f(D* d) {
return d->x(); // ambiguous: B: or C:
}
My first question is, Why can't the using declaration be used to perform
this kind of disambiguation? If the using declaration declares function x
in struct D as a "synonym" for C: , it seems that it would be reasonable
to
expect that d->x() would call C: , just as d->C: () would.
|
The only effect of a using-declaration (apart from overload
resolution) is to prevent hiding of base class names by
derived class declarations. It doesn't do anything else.
In particular, the two using-declarations in the example
above just mean that A: () is not hidden by the declarations
in C and D. However, there are two subobjects of type A, so
the lookup set has two A: () members. (See 10.2p2: "Each of
these declarations that was introduced by a using-declaration
is considered to be from each sub-object of C that is of the
type containing the declaration designated by the
using-declaration.")
Another way of looking at this is that using-declarations
deal only with types, not objects (cf "each sub-object ...
of the _type_ containing the declaration..." in the preceding
citation [emphasis added]).
| Quote: | In spite of the Standard's general refusal to allow the using declaration
to
resolve inherited member ambiguities, several compilers (Comeau 4.3.0.1,
g++
3.2, Borland 5.5.1) allow the following:
#include
class X { public: char x(){ return 'X';} };
class Y { public: char x(){ return 'Y';} };
class Z : public X, public Y {
public:
using X: ;
};
int main() {
Z z;
assert( z.x() == 'X' );
}
My second question is, Does the Standard allow this behavior?
|
Yes. The using-declaration hides Y: (), and there's only one
subobject of type X in Z, so there's no ambiguity.
-- William M. Miller
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
William Hayes Guest
|
Posted: Thu Jul 10, 2003 5:42 pm Post subject: Re: Using declarations and class member ambiguities |
|
|
"Daveed Vandevoorde" <google (AT) vandevoorde (DOT) com> wrote
| Quote: | whayes (AT) hfx (DOT) andara.com ("William Hayes") wrote:
The Standard says that "...because a using-declaration designates a base
class member (and not a member subobject or a member function of a base
class subobject), a using-declaration cannot be used to resolve
inherited
member ambiguities" (7.3.3, 14). The example it cites involves an
inheritance hierarchy containing multiple copies of a base class:
struct A { int x(); };
struct B : A { };
struct C: A {
using A: ;
int x(int);
};
struct D : B, C {
using C: ;
int x(double);
};
int f(D* d) {
return d->x(); // ambiguous: B: or C:
}
My first question is, Why can't the using declaration be used to perform
this kind of disambiguation? If the using declaration declares function
x
in struct D as a "synonym" for C: , it seems that it would be
reasonable to
expect that d->x() would call C: , just as d->C: () would.
A using declaration is a mechanism to direct lookup. To process the
expression "d->x()" a compiler has to do several things, among which:
(a) look up x.
(b) perform overload resolution if there are multiple x.
(c) convert d to the appropriate type to serve as a "this"
pointer for the selected x.
Using declarations only affect (a); not (c) (or (b), obviously).
In you example, the using declarations cause the overload set (i.e.,
the result of looking up x) to consist of:
A: ()
B: (int)
C: (double)
Overload resolution easily picks A: (), which means that d must
be converted to an A*, and it is that part that is ambiguous.
In spite of the Standard's general refusal to allow the using
declaration to
resolve inherited member ambiguities, several compilers (Comeau 4.3.0.1,
g++
3.2, Borland 5.5.1) allow the following:
#include
class X { public: char x(){ return 'X';} };
class Y { public: char x(){ return 'Y';} };
class Z : public X, public Y {
public:
using X: ;
};
int main() {
Z z;
assert( z.x() == 'X' );
}
My second question is, Does the Standard allow this behavior?
Sure, applying the principles above, the overload set is a singleton:
X: ()
Overload resolution is trivial, and the conversion of the Z& to X& is
unambiguous.
|
So, class member lookup returns (in effect) a list of qualified class member
names. Each name on the list specifies a class that actually contains a
declaration of that member (e.g., A: ), never a derived class that merely
inherited one (e.g., C: ). Is that correct?
Does the prohibition against using the using declaration for this kind of
disambiguation therefore reflect the way name lookup works, rather than a
design decision related to the using declaration itself?
Thanks.
__________________________
William Hayes
Raven Software Limited
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
William Hayes Guest
|
Posted: Fri Jul 11, 2003 1:39 am Post subject: Re: Using declarations and class member ambiguities |
|
|
""William M. Miller"" <wmm (AT) world (DOT) std.com> wrote
| Quote: | ""William Hayes"" <whayes (AT) hfx (DOT) andara.com> wrote in message
news:jFXMa.33361$PD3.111394 (AT) nnrp1 (DOT) uunet.ca...
The Standard says that "...because a using-declaration designates a base
class member (and not a member subobject or a member function of a base
class subobject), a using-declaration cannot be used to resolve
inherited
member ambiguities" (7.3.3, 14). The example it cites involves an
inheritance hierarchy containing multiple copies of a base class:
struct A { int x(); };
struct B : A { };
struct C: A {
using A: ;
int x(int);
};
struct D : B, C {
using C: ;
int x(double);
};
int f(D* d) {
return d->x(); // ambiguous: B: or C:
}
My first question is, Why can't the using declaration be used to perform
this kind of disambiguation? If the using declaration declares function
x
in struct D as a "synonym" for C: , it seems that it would be
reasonable
to
expect that d->x() would call C: , just as d->C: () would.
The only effect of a using-declaration (apart from overload
resolution) is to prevent hiding of base class names by
derived class declarations. It doesn't do anything else.
In particular, the two using-declarations in the example
above just mean that A: () is not hidden by the declarations
in C and D. However, there are two subobjects of type A, so
the lookup set has two A: () members. (See 10.2p2: "Each of
these declarations that was introduced by a using-declaration
is considered to be from each sub-object of C that is of the
type containing the declaration designated by the
using-declaration.")
Another way of looking at this is that using-declarations
deal only with types, not objects (cf "each sub-object ...
of the _type_ containing the declaration..." in the preceding
citation [emphasis added]).
In spite of the Standard's general refusal to allow the using
declaration
to
resolve inherited member ambiguities, several compilers (Comeau 4.3.0.1,
g++
3.2, Borland 5.5.1) allow the following:
#include
class X { public: char x(){ return 'X';} };
class Y { public: char x(){ return 'Y';} };
class Z : public X, public Y {
public:
using X: ;
};
int main() {
Z z;
assert( z.x() == 'X' );
}
My second question is, Does the Standard allow this behavior?
Yes. The using-declaration hides Y: (), and there's only one
subobject of type X in Z, so there's no ambiguity.
|
Do all of the effects of the using declaration on member lookup boil down to
effects on name hiding? In the first example, the using declarations pull
C: (int) and A: () into the scope of D (in a sense), preventing
D: (double) from hiding them. In the second, the using declaration pulls
X: into the scope of Z, causing X: to hide Y: instead of conflicting
with it.
When you mention the effect of using declarations on overload resolution,
are you referring to its indirect effect, through its influence on the
composition of the lookup set?
Thanks.
__________________________
William Hayes
Raven Software Limited
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
William M. Miller Guest
|
Posted: Sat Jul 12, 2003 12:15 am Post subject: Re: Using declarations and class member ambiguities |
|
|
""William Hayes"" <whayes (AT) hfx (DOT) andara.com> wrote
| Quote: | Do all of the effects of the using declaration on member lookup boil down
to
effects on name hiding? In the first example, the using declarations pull
C: (int) and A: () into the scope of D (in a sense), preventing
D: (double) from hiding them. In the second, the using declaration pulls
X: into the scope of Z, causing X: to hide Y: instead of conflicting
with it.
|
Yes. Of course, there are also effects on access and overloading,
but in terms of name lookup, it's just for circumventing hiding.
(It's different with namespace using-declarations, where visibility
is the principal effect, but since base class members are visible
in derived classes unless hidden, for member using-declarations it's
all about avoiding hiding.)
| Quote: | When you mention the effect of using declarations on overload resolution,
are you referring to its indirect effect, through its influence on the
composition of the lookup set?
|
No, I was thinking of 7.3.3p13 -- the implicit "this" parameter of
a non-static member function is assumed to be the type of the class
in which the using-declaration appears, not that of the class of
which it is directly a member. Here's an example where that makes
a difference:
struct B {
void f(char);
};
struct D: B {
using B::f;
void f(int);
};
void x(D* dp) {
dp->f('c');
}
If it weren't for this rule, the call "dp->f('c')" would be
ambiguous -- the 'c' is a better match for B::f(char), but "dp" would
be a better match for D::f(int). Because of the using-declaration,
B::f(char) is treated for overload resolution as if its "this" were
a D* instead of a B*, allowing the two member functions to compete
solely on the basis of their explicit parameter types.
-- William M. Miller
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|