Updated description

[SVN r17898]
This commit is contained in:
Terje Slettebø
2003-03-13 22:01:48 +00:00
parent 138df12e67
commit 3f87dccb57

View File

@ -1,5 +1,5 @@
// (C) Copyright Rani Sharoni 2002.
// (C) Copyright Rani Sharoni 2003.
// Permission to copy, use, modify, sell and distribute this software is
// granted provided this copyright notice appears in all copies. This software
// is provided "as is" without express or implied warranty, and with no claim
@ -35,58 +35,73 @@ namespace detail {
This version detects ambiguous base classes and private base classes
correctly, and was devised by Rani Sharoni.
The following explanation is by Terje Slettebo:
Explanation by Terje Sletteb<EFBFBD> and Rani Sharoni.
Let's take the multiple base class below as an example, and the
following will also show why there's not a problem with ambiguous base
Let's take the multiple base class below as an example, and the following
will also show why there's not a problem with private or ambiguous base
class:
struct B {};
struct B1 : B {};
struct B2 : B {};
struct D : B1, private B2 {};
struct D : private B1, private B2 {};
typedef char Test[is_base_and_derived<B, D>::result]; // improvement 1 -
multiple base
is_base_and_derived<B, D>::value;
First, some terminology:
We have several possible conversion sequences:
SC - Standard conversion
UDC - User-defined conversion
For "static no check(B const volatile *, int)" we have the conversion
A user-defined conversion sequence consists of an SC, followed by an UDC,
followed by another SC. Either SC may be the identity conversion.
When passing the default-constructed Host object to the overloaded check()
functions (initialization 8.5/14/4/3), we have several viable implicit
conversion sequences:
For "static no_type check(B const volatile *, int)" we have the conversion
sequences:
C -> C const -> B*
and
C -> D* -> B1*|B2* -> B*
For "static yes check(D const volatile *, T)" we have the conversion
C -> C const (SC - Qualification Adjustment) -> B const volatile* (UDC)
C -> D const volatile* (UDC) -> B1 const volatile*/B2 const volatile* ->
B const volatile* (SC - Conversion)
For "static yes_type check(D const volatile *, T)" we have the conversion
sequence:
C -> D*
Since, for the purpose of selecting the appropriate user-defined conversion
for a given function, it only considers up to the user-defined conversion,
for the first function this means choosing between C -> C const and C -> C,
and it chooses the latter. Therefore, we have:
C -> D const volatile* (UDC)
C -> D* -> B1*|B2* -> B*
C -> D*
According to 13.3.3.1/4, in context of user-defined conversion only the
standard conversion sequence is considered when selecting the best viable
function, so it only considers up to the user-defined conversion. For the
first function this means choosing between C -> C const and C -> C, and it
chooses the latter, because it's a proper subset (13.3.3.2/3/2) of the
former. Therefore, we have:
Here, the principle of the "shortest subsequence" applies, and it chooses
C -> D*. This shows that it doesn't even need to consider the multiple paths
to B, as that possibility is eliminated before it could possibly cause
ambiguity. Nifty. :)
C -> D const volatile* (UDC) -> B1 const volatile*/B2 const volatile* ->
B const volatile* (SC - Conversion)
C -> D const volatile* (UDC)
As Daveed notes in the posting Rani gives a link to in the clc++m posting,
if D is not derived from B, it has to choose between C -> C const -> B* for
the first function, and C -> D* for the second function, which are just as
good, _had it not been for the fact that "static no check(B const volatile
&, int)" is not templated (as Rani points out in the posting)_, which makes
C -> C const -> B* the best choice, resulting in "no".
Here, the principle of the "shortest subsequence" applies again, and it
chooses C -> D const volatile*. This shows that it doesn't even need to
consider the multiple paths to B, or accessibility, as that possibility is
eliminated before it could possibly cause ambiguity or access violation.
Also, if C::operator B* hadn't been const, the two conversion sequences for
"static no check(B const volatile *, int)" would have been ambiguous.
If D is not derived from B, it has to choose between C -> C const -> B const
volatile* for the first function, and C -> D const volatile* for the second
function, which are just as good (both requires a UDC, 13.3.3.2), had it not
been for the fact that "static no_type check(B const volatile *, int)" is
not templated, which makes C -> C const -> B const volatile* the best choice
(13.3.3/1/4), resulting in "no".
See also http://groups.google.com/groups?selm=df893da6.0301280859.522081f7%40posting.google.com
and links therein.
Also, if Host::operator B const volatile* hadn't been const, the two
conversion sequences for "static no_type check(B const volatile *, int)", in
the case where D is derived from B, would have been ambiguous.
See also
http://groups.google.com/groups?selm=df893da6.0301280859.522081f7%40posting.
google.com and links therein.
*************************************************************************/
@ -189,5 +204,3 @@ BOOST_TT_AUX_BOOL_TRAIT_PARTIAL_SPEC2_2(typename Base,typename Derived,is_base_a
#include "boost/type_traits/detail/bool_trait_undef.hpp"
#endif // BOOST_TT_IS_BASE_AND_DERIVED_HPP_INCLUDED