Topic: Implementing check_cast
Author: Scott Meyers <smeyers@aristeia.com>
Date: Mon, 21 Jan 2002 08:33:08 GMT Raw View
On Fri, 18 Jan 2002 18:17:12 CST, Beman Dawes wrote:
> Did you look at boost::polymorphic_cast<>() and
> boost::polymorphic_downcast<>()? See
> http://www.boost.org/libs/conversion/cast.htm
Yes, but they seem to consider only pointers. I'm interested in reference
casts, too.
Scott
--
Check out the *new* "THE C++ Seminar,"
http://www.gotw.ca/cpp_seminar/
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: Scott Meyers <smeyers@aristeia.com>
Date: Wed, 16 Jan 2002 15:54:47 GMT Raw View
On Mon, 14 Jan 2002 16:01:02 GMT, Michiel Salters wrote:
> Yes, P is determined. But note that the function templare contains a
> parameter depending on P, in the place where the function call
> contains an A. That parameter, let's call it A'(P), might be P or P*
> or some other type based on P. We want to find a P such that A'(P)==A.
Ah. Now *that* makes sense. Thank you! I was going to add your
explanation as an annotation to that section of my copy of the standard,
but I see that either ISO has prohibited annotations or I'm too dumb to
know how to add them with Acrobat 4. (If somebody knows how, please tell
me.)
.8.2.1 also states that if A is a reference type, we may generate
> a cv-qualified version instead. This is logical: f( A const& ) will
> accept an A&.
>
> Back to your case: A is a Base const&.
> There are two possible matches.
> The first template: Because the signature is X&, and the type to
> match is Base const&, X must be const Base.
Isn't the type to match actually Base const (without the reference)? I
have no idea where this is in the Standard, but Langer and Kreft wrote the
following in their column at
http://www.cuj.com/experts/1812/langer.htm?topic=experts, and tbey
generally know what they are talking about:
...the compiler instantiates the inner function template for the value
type Base instead of the reference type Base&. This might be surprising,
but is intended: automatic function argument deduction involves a number
of implicit type conversions, one of which is the lvalue-to-rvalue
transformation...
On the other hand, the same article contains this:
[The compiler] drops const qualifications and would never instantiate a
function template for a const type, but always for the corresponding
non-const type.
That suggests that an actual argument of type Base const& would yield a
deduced type of Base. But I'm probably just continuing to be confused.
> pointing out a flaw in my logic, but the wording in 14.5.5.2 is
> pretty clear.
I've stumbled into that part of the Standard before. Your vision is MUCH
better than mine. (There's no need to explain, as others have kindly done
it before.)
> I'm wondering why you want the specialized version in the first place.
Which specialization are you referring to? From what I can tell, I need a
version of check_cast for pointers, for non-pointer lvalues, and for
non-pointer rvalues.
Scott
--
Check out the *new* "THE C++ Seminar,"
http://www.gotw.ca/cpp_seminar/
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: Nathan Sidwell <nathan@acm.org>
Date: Wed, 16 Jan 2002 16:01:10 GMT Raw View
This is a multi-part message in MIME format.
--------------7A23E5780E978BE1AAC6B1D6
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Scott Meyers wrote:
>
> My goal is really simple. I want to create a template called check_cast
> that expands into a dynamic_cast during debug builds and into a static_cast
> during release builds. That is, if I type
>
> check_cast<TYPE>(EXPR)
this looks stunningly like G++ bug report 4672
(http://gcc.gnu.org/cgi-bin/gnatsweb.pl?cmd=view&pr=4672)
> Consider, however, the following case where const is involved:
> About this last line, g++ says this:
> This suggests that g++ can't decide between the type two possible type
> deductions for check_cast's formal type parameter X. Should X be Base or
> should it be const Base?
No that's not the problem. The two valid deductions are unordered
according
to the partial ordering rules, as neither can deduce T from the other.
T check_cast(X&) [with T = const Der&, X = const Base]
T check_cast(const X&) [with T = const Der&, X = Base]
The non-const case does not suffer the same problem as X=Base is the
only valid deduction for that.
Defect reports 200 & 214 talk about this problem, but do not suggest a
resolution. I drafted the attached set of comments after implementing a
patch
to g++ to do the sensible thing (IMO, of course). This has been
forwarded
to the std committee.
The patch I prepared is unlikely to get into g++ until there's some
consensus that it is the correct resolution.
nathan
--
Dr Nathan Sidwell :: Computer Science Department :: Bristol University
The voices in my head told me to say this
nathan@acm.org http://www.cs.bris.ac.uk/~nathan/ nathan@cs.bris.ac.uk
--------------7A23E5780E978BE1AAC6B1D6
Content-Type: text/plain; charset=us-ascii;
name="4672"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="4672"
Hi,
Defect reports 200 & 214 (final point), mention the ambiguity of
template function partial ordering in the presence of undeduced
parameters provided by a template-id. However there is no discussion
on any proposed resolution. I drafted the attached report, before I
noticed that. I provide a suggested resolution, which may be of
interest. I also have a sample implementation in the form of a patch
to g++, which is archived at
http://gcc.gnu.org/ml/gcc-patches/2001-12/msg02641.html, should any
one care to experiment.
nathan
--
Dr Nathan Sidwell :: http://www.codesourcery.com :: CodeSourcery LLC
'But that's a lie.' - 'Yes it is. What's your point?'
nathan@codesourcery.com : http://www.cs.bris.ac.uk/~nathan/ : nathan@acm.org
Submitter: Nathan Sidwell
Description:
[temp.func.order] describes the partial ordering of function
templates. Paragraph 5 states,
'A template is more specialized than another if, and
only if, it is at least as specialized as the other
template and that template is not at least as
specialized as the first.'
To paraphrase, given two templates A & B, if A's template
parameters can be deduced by B, but B's cannot be deduced by
A, then A is more specialized than B. Deduction is done as
if for a function call. In particular, except for conversion
operators, the return type is not involved in deduction.
This leads to the following templates and use being
unordered. (This example is culled from G++ bug report 4672
http://gcc.gnu.org/cgi-bin/gnatsweb.pl?cmd=view&pr=4672)
template <typename T, class U> T checked_cast(U from); //#1
template <typename T, class U> T checked_cast(U * from); //#2
class C {};
void foo (int *arg)
{
checked_cast <C const *> (arg);
}
In the call,
#1 can be deduced with T = 'C const *' and U = 'int *'
#2 can be deduced with T = 'C const *' and U = 'int'
It looks like #2 is more specialized that #1, but
[temp.func.order] does not make it so, as neither template can
deduce 'T' from the other template's function parameters.
Possible Resolutions:
There are several possible solutions, however through
experimentation I have discounted two of them.
Option 1:
When deducing function ordering, if the return type of one of
the templates uses a template parameter, then return types
should be used for deduction. This, unfortunately, makes
existing well formed programs ill formed. For example
template <class T> class X {};
template <class T> X<T> Foo (T *); // #1
template <class T> int Foo (T const *); // #2
void Baz (int *p1, int const *p2)
{
int j = Foo (p2); //#3
}
Here, neither #1 nor #2 can deduce the other, as the return
types fail to match. Considering only the function parameters
gives #2 more specialized than #1, and hence makes the call
#3 well formed.
Option 2:
As option 1, but only consider the return type when deducing
the template whose return type involves template parameters.
This has the same flaw as option 1, and that example is
similarly ill formed, as #1's return type 'X<T,0>' fails to
match 'int' so #1 cannot deduce #2. In the converse direction,
return types are not considered, but the function parameters
fail to deduce.
Option 3:
It is observed that the orginal example is only callable
with a template-id-expr to supply a value for the first,
undeducible, parameter. If that parameter were deducible it
would also appear within at least one of the function
parameters. We can alter paragraph 4 of [temp.func.order]
to indicate that it is not necessary to deduce the parameters
which are provided explicitly, when the call has the form
of a template-id-expr. This is a safe extension as it only
serves to make ill formed programs well formed. It is also
in line with the concept that deduction for function
specialization order should proceed in a similar manner to
function calling, in that explicitly provided parameter
values are taken into consideration.
Suggested resolution:
Insert after the first sentance of paragraph 4 in
[temp.function.order]
'Should any template parameters remain undeduced, and
the function call be of the form of a template-id-expr,
those template parameters provided in the
template-id-expr may be arbitrarily synthesized prior
to determining whether the deduced arguments generate
a valid function type.'
--------------7A23E5780E978BE1AAC6B1D6--
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: Scott Meyers <smeyers@aristeia.com>
Date: Wed, 16 Jan 2002 18:12:31 GMT Raw View
On Mon, 14 Jan 2002 16:23:56 GMT, Peter Dimov wrote:
> This is a known issue with the partial ordering rules, f(T &) and f(T
> const &) cannot be ordered. See
>
> http://groups.google.com/groups?selm=3B0EEF21%40MailAndNews.com
This URL doesn't work for me. Can you please provide a thread subject and
approximate date?
> This makes it really difficult to write functions that take anything.
> f(T &) by itself fails for non-const rvalues, and the combined
> approach fails for const l- or rvalues. Even if you don't care about
> the const-ness, f(T const &) fails for functions (although there is a
> defect report against it.)
Do you happen to know the issue number?
Thanks,
Scott
--
Check out the *new* "THE C++ Seminar,"
http://www.gotw.ca/cpp_seminar/
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: pdimov@mmltd.net (Peter Dimov)
Date: Thu, 17 Jan 2002 16:10:33 GMT Raw View
Scott Meyers <smeyers@aristeia.com> wrote in message news:<MPG.16aecdaa607afcce9896b3@news.hevanet.com>...
> On Mon, 14 Jan 2002 16:23:56 GMT, Peter Dimov wrote:
> > This is a known issue with the partial ordering rules, f(T &) and f(T
> > const &) cannot be ordered. See
> >
> > http://groups.google.com/groups?selm=3B0EEF21%40MailAndNews.com
>
> This URL doesn't work for me. Can you please provide a thread subject and
> approximate date?
Strange. Works for me.
From: Peter Dimov (pdimov@MailAndNews.com)
Subject: T & vs T const & overloading
Newsgroups: comp.std.c++
Date: 2001-05-16 08:16:02 PST
The gist of it is that there are two possible interpretations of
14.5.5.2. It basically says that given
template<class T> void f(T &);
template<class U> void f(U const &);
int main()
{
int const x = 5;
f(x);
}
which function template is more specialized is determined by
synthesizing unique arguments from the first and trying to match them
with the second, and vice versa.
The problem is that some - notably EDG frontend authors - interpret
this as synthesizing values and performing deduction as in a function
call (14.8.2.1):
[1] f<T>(T &) -> f<U>(U const &)
T == __unique_type_1
argument == __unique_type_1 &
matching against f<T>(T const &): success, U == __unique_type_1
[2] f<U>(U const &) -> f<T>(T &)
U == __unique_type_2
argument == __unique_type_2 const &
matching against f<T>(T &): success, T == __unique_type_2 const
Ambiguity.
Others interpret this to mean "deduction as in 14.8.2.4":
[1] f(T &) -> f<U>(U const &)
Failure, U const cannot match T for an arbitrary T.
[2] f(U const &) -> f<T>(T &)
Success, T == U const, f<T>(T &) is a superset of f<U>(U const &),
therefore the latter is more specialized. (This goes contrary to the
non-template case where f(int &) is a better match than f(int const
&).)
The relevant issue is CWG #214,
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_active.html#214
Your original example is even more problematic since it has an
additional non-deducible template parameter, and 14.5.5.2 doesn't
cover this case at all (closed issue 200.)
> > This makes it really difficult to write functions that take anything.
> > f(T &) by itself fails for non-const rvalues, and the combined
> > approach fails for const l- or rvalues. Even if you don't care about
> > the const-ness, f(T const &) fails for functions (although there is a
> > defect report against it.)
>
> Do you happen to know the issue number?
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_active.html#295
--
Peter Dimov
Multi Media Ltd.
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: Michiel.Salters@cmg.nl (Michiel Salters)
Date: Thu, 17 Jan 2002 16:10:08 GMT Raw View
Scott Meyers <smeyers@aristeia.com> wrote in message news:<MPG.16aec4d9153ad7ba9896b2@news.hevanet.com>...
> On Mon, 14 Jan 2002 16:01:02 GMT, Michiel Salters wrote:
> > .8.2.1 also states that if A is a reference type, we may generate
> > a cv-qualified version instead. This is logical: f( A const& ) will
> > accept an A&.
> >
> > Back to your case: A is a Base const&.
> > There are two possible matches.
> > The first template: Because the signature is X&, and the type to
> > match is Base const&, X must be const Base.
>
> Isn't the type to match actually Base const (without the reference)? I
> have no idea where this is in the Standard, but Langer and Kreft wrote the
> following in their column at
> http://www.cuj.com/experts/1812/langer.htm?topic=experts, and tbey
> generally know what they are talking about:
>
> ...the compiler instantiates the inner function template for the value
> type Base instead of the reference type Base&. This might be surprising,
> but is intended: automatic function argument deduction involves a number
> of implicit type conversions, one of which is the lvalue-to-rvalue
> transformation...
I can't find any wording that says so, either. The standard list exactly
three transformations on A, the argument, and lvalue to rvalue is not
one of them.
But I don't think it matters. X is Base const, in either case.
> On the other hand, the same article contains this:
>
> [The compiler] drops const qualifications and would never instantiate a
> function template for a const type, but always for the corresponding
> non-const type.
>
> That suggests that an actual argument of type Base const& would yield a
> deduced type of Base. But I'm probably just continuing to be confused.
That would refer to /2, "If P is not a reference type:— If A is a
cv-qualified type, the top level cv-qualifiers of A’s type are
ignored". Which is reasonable; if P is not a reference type, the
function will use call-by-value. So the Base const& will be copied.
There is no reason why a Base const& should be copied to a const
Base instead of a plain Base.
Call-by-reference is different, there is a major reason why you
shouldn't initialize a plain Base& parameter with a Base const&
> > I'm wondering why you want the specialized version in the first place.
>
> Which specialization are you referring to? From what I can tell, I need a
> version of check_cast for pointers, for non-pointer lvalues, and for
> non-pointer rvalues.
> Scott
I'd better snip the part about clear vision; I missed the point about
rvalues. Perhaps there should be a rule that rvalues of type A,
when passed to functions with argument P&, would cause P to be deduced
as A const.
Regards,
--
Michiel Salters
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: beman_@hotmail.com (Beman Dawes)
Date: Fri, 18 Jan 2002 18:17:12 CST Raw View
Scott Meyers <smeyers@aristeia.com> wrote in message news:<MPG.16a97e6c2ab805199896ad@news.hevanet.com>...
> My goal is really simple. I want to create a template called check_cast
> that expands into a dynamic_cast during debug builds and into a static_cast
> during release builds. That is, if I type
>
> check_cast<TYPE>(EXPR)
>
> it should mean one of these:
>
> dynamic_cast<TYPE>(EXPR) // in debug builds
> static_cast<TYPE>(EXPR) // in release builds
>
> There is one minor twist. If check_cast expands into a dynamic_cast on a
> pointer and the cast fails, I want to throw a std::bad_cast exception, just
> as I would if a reference cast failed.
Did you look at boost::polymorphic_cast<>() and
boost::polymorphic_downcast<>()? See
http://www.boost.org/libs/conversion/cast.htm
--Beman
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: Scott Meyers <smeyers@aristeia.com>
Date: Sat, 12 Jan 2002 10:03:01 CST Raw View
My goal is really simple. I want to create a template called check_cast
that expands into a dynamic_cast during debug builds and into a static_cast
during release builds. That is, if I type
check_cast<TYPE>(EXPR)
it should mean one of these:
dynamic_cast<TYPE>(EXPR) // in debug builds
static_cast<TYPE>(EXPR) // in release builds
There is one minor twist. If check_cast expands into a dynamic_cast on a
pointer and the cast fails, I want to throw a std::bad_cast exception, just
as I would if a reference cast failed.
After playing around a bit and picking various people's brains (notably
Andrei Alexandrescu's and Steve Dewhurst's), I came up with the following.
(I've left my debugging code in in case you want to play with it.)
template<typename T> struct IsPtr { enum { value = false }; };
template<typename T> struct IsPtr<T*> { enum { value = true }; };
template<typename T, typename X, bool isPtr>
struct check_cast_impl;
template<typename T, typename X>
struct check_cast_impl<T, X, true> {
static T cast(X x) {
std::cout << "Calling pointer version...";
#ifdef DEBUG_STREAM
T t = dynamic_cast<T>(x);
if (!t) throw std::bad_cast();
return t;
#else
return static_cast<T>(x);
#endif
}
};
template<typename T, typename X>
struct check_cast_impl<T, X, false> {
static T cast(X& x) {
std::cout << "Calling non-pointer version...";
#ifdef DEBUG_STREAM
return dynamic_cast<T>(x);
#else
return static_cast<T>(x);
#endif
}
};
template<typename T, typename X>
inline T check_cast(X& x)
{
std::cout << "Calling check_cast(X&)...";
return check_cast_impl<T, X, IsPtr<X>::value>::cast(x);
}
template<typename T, typename X>
inline T check_cast(const X& x)
{
std::cout << "Calling check_cast(const X&)...";
return check_cast_impl<T, X, IsPtr<X>::value>::cast(x);
}
This compiles under g++ 2.95.2, Comeau 4.2.45.2, and MW 6.1, and it does
what I want it to do under g++ and Comeau for expressions where const isn't
involved. (Under MW 6.1, it binds an rvalue expression to the check_cast
template taking an X& instead of a const X&, but I think that's a bug in MW
6.1, and I'm not too concerned about that right now.)
Consider, however, the following case where const is involved:
class Base {
public:
virtual ~Base(){}
};
class Der: public Base {};
const Base *pb = new Der;
const Der& rd = check_cast<const Der&>(*pb);
About this last line, g++ says this:
check_cast.cpp:97: call of overloaded `check_cast (const Base &)' is
ambiguous
check_cast.cpp:42: candidates are: const class Der & check_cast<const Der &,
const Base>(const Base &)
check_cast.cpp:49: const class Der & check_cast<const Der &,
Base>(const Base &)
This suggests that g++ can't decide between the type two possible type
deductions for check_cast's formal type parameter X. Should X be Base or
should it be const Base?
MW says this:
# 51: return check_cast_impl<T, X, IsPtr<X>::value>::cast(x);
# Error: ^
# illegal implicit conversion from 'const Base' to
# 'Base &'
# (instantiating: 'check_cast<const Der &, Base>(const Base &)')
This suggests that MW has deduced that X = Base.
Comeau says this:
"check_cast.cpp", line 97: error: more than one instance of overloaded
function "check_cast" matches the argument list:
function template "check_cast<T,X>(X &)"
function template "check_cast<T,X>(const X &)"
argument types are: (const Base)
const Der& rd = check_cast<const Der&>(*pb);
This suggests that it has deduced that X = const Base.
So MW says X = Base, Comeau says X = const Base, and g++ says it can't
decide. Sigh.
I have two questions, one standards-related, one not:
1. What should a conforming compiler deduce X to be? I've tried to read
14.8.2.1 many times, and I simply cannot make heads or tails of it.
For example, when I read this,
In general, the deduction process attempts to find template argument
values that will make the deduced A identical to A (after the type A
is transformed as described above).
my head spins, especially because I was under the impression that
deduction was supposed to determine the type for P, not A.
2. Maybe I'm approaching this problem the wrong way. Is there a better
way to have check_cast exhibit the behavior that I want? Please note
that I don't want to change the syntax, so I don't think it's possible
to turn check_cast into a macro.
Thanks, and I'm sorry this is so long.
Scott
--
Check out the *new* "THE C++ Seminar,"
http://www.gotw.ca/cpp_seminar/
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: Michiel.Salters@cmg.nl (Michiel Salters)
Date: Mon, 14 Jan 2002 16:01:02 GMT Raw View
Scott Meyers <smeyers@aristeia.com> wrote in message news:<MPG.16a97e6c2ab805199896ad@news.hevanet.com>...
> My goal is really simple.
I'm not going to respond to this :)
> I want to create a template called check_cast that expands into
> a dynamic_cast during debug builds and into a static_cast
> during release builds. That is, if I type
>
> check_cast<TYPE>(EXPR)
>
> it should mean one of these:
>
> dynamic_cast<TYPE>(EXPR) // in debug builds
> static_cast<TYPE>(EXPR) // in release builds
>
[SNIP]
> I came up with the following.
>
> template<typename T> struct IsPtr { enum { value = false }; };
> template<typename T> struct IsPtr<T*> { enum { value = true }; };
>
> template<typename T, typename X, bool isPtr>
> struct check_cast_impl;
>
> template<typename T, typename X>
> struct check_cast_impl<T, X, true> {
> static T cast(X x) {
> std::cout << "Calling pointer version...";
> #ifdef DEBUG_STREAM
> T t = dynamic_cast<T>(x);
> if (!t) throw std::bad_cast();
> return t;
> #else
> return static_cast<T>(x);
> #endif
> }
> };
>
> template<typename T, typename X>
> struct check_cast_impl<T, X, false> {
> static T cast(X& x) {
> std::cout << "Calling non-pointer version...";
> #ifdef DEBUG_STREAM
> return dynamic_cast<T>(x);
> #else
> return static_cast<T>(x);
> #endif
> }
> };
>
> template<typename T, typename X>
> inline T check_cast(X& x)
> {
> std::cout << "Calling check_cast(X&)...";
> return check_cast_impl<T, X, IsPtr<X>::value>::cast(x);
> }
>
> template<typename T, typename X>
> inline T check_cast(const X& x)
> {
> std::cout << "Calling check_cast(const X&)...";
> return check_cast_impl<T, X, IsPtr<X>::value>::cast(x);
> }
Ok, so check_cast_impl exists to implement different behavior for
pointers. That check_cast requires T::T(const& T) is probably Ok,
but formally, this isn't required for either static_cast<> or
dynamic_cast<>.
> This compiles under g++ 2.95.2, Comeau 4.2.45.2, and MW 6.1, and it does
> what I want it to do under g++ and Comeau for expressions where const isn't
> involved. (Under MW 6.1, it binds an rvalue expression to the check_cast
> template taking an X& instead of a const X&, but I think that's a bug in MW
> 6.1, and I'm not too concerned about that right now.)
>
> Consider, however, the following case where const is involved:
>
> class Base {
> public:
> virtual ~Base(){}
> };
>
> class Der: public Base {};
>
> const Base *pb = new Der;
> const Der& rd = check_cast<const Der&>(*pb);
>
> About this last line, g++ says this:
[SNIP]
> This suggests that g++ can't decide between the type two possible type
> deductions for check_cast's formal type parameter X. Should X be Base or
> should it be const Base?
> MW says this:
[SNIP]
> This suggests that MW has deduced that X = Base.
>
> Comeau says this:
[SNIP]
> This suggests that it has deduced that X = const Base.
> So MW says X = Base, Comeau says X = const Base, and g++ says it can't
> decide. Sigh.
> I have two questions, one standards-related, one not:
>
> 1. What should a conforming compiler deduce X to be? I've tried to read
> 14.8.2.1 many times, and I simply cannot make heads or tails of it.
> For example, when I read this,
>
> In general, the deduction process attempts to find template argument
> values that will make the deduced A identical to A (after the type A
> is transformed as described above).
>
> my head spins, especially because I was under the impression that
> deduction was supposed to determine the type for P, not A.
Yes, P is determined. But note that the function templare contains a
parameter depending on P, in the place where the function call
contains an A. That parameter, let's call it A'(P), might be P or P*
or some other type based on P. We want to find a P such that A'(P)==A.
14.8.2.1 also states that if A is a reference type, we may generate
a cv-qualified version instead. This is logical: f( A const& ) will
accept an A&.
Back to your case: A is a Base const&.
There are two possible matches.
The first template: Because the signature is X&, and the type to
match is Base const&, X must be const Base.
The second template: The signature here is X const&, the type
to match is Base const& so X==Base.
Now, both templates can be used to generate viable functions.
So we need overload resolution to determine which one is chosen. This
is the subject of 14.8.3[temp.over]. This points out that overloading
is covered in 13.3.3[over.match.best]. Indeed, 13.3.3 says which
check_cast overload is to be chosen; namely the one that "is more
specialized [...] according to the partial ordering rules
described in 14.5.5.2". Continuing out tour of the standard, in
14.5.5.2[temp.func.order] we see that the second template
( check_cast( X const& ) ) is more specialized and should be
chosen. I admit, the example is a lot easier than the text.
The point is that given check_cast( T& ) and check_cast( const U &),
it is possible to find a T so that T==const U, but it is not always
possible to find a U such that T==const U. So the template
check_cast(const U&) is more specialized (works in less cases).
And as a result, the deduced type should be Base, not const Base.
So that would mean that MW is right, and Comeau(EDG) is wrong.
I know that stating that last part usually is followed by someone
pointing out a flaw in my logic, but the wording in 14.5.5.2 is
pretty clear.
> 2. Maybe I'm approaching this problem the wrong way. Is there a better
> way to have check_cast exhibit the behavior that I want? Please note
> that I don't want to change the syntax, so I don't think it's possible
> to turn check_cast into a macro.
I'm wondering why you want the specialized version in the first place.
The only thing the specialization does is to strip the const-ness of
any X parameter. Your template IsPtr<> will work on const pointers
too, IsPtr<const int*>::value==true. And passing the const-ness of
your parameter to static_cast/dynamic_cast shouldn't hurt either.
But I'm not sure if I get your design goals.
I guess you might need the const-ness if you decide to eliminate the
requirement for a copy constructor. That would require you to return
either a T const& or a T&, based on the const-ness of your argument.
Regards,
--
Michiel Salters
---
[ 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.research.att.com/~austern/csc/faq.html ]
Author: pdimov@mmltd.net (Peter Dimov)
Date: Mon, 14 Jan 2002 16:23:56 GMT Raw View
Scott Meyers <smeyers@aristeia.com> wrote in message news:<MPG.16a97e6c2ab805199896ad@news.hevanet.com>...
[...]
> template<typename T, typename X>
> inline T check_cast(X& x)
> {
> std::cout << "Calling check_cast(X&)...";
> return check_cast_impl<T, X, IsPtr<X>::value>::cast(x);
> }
>
> template<typename T, typename X>
> inline T check_cast(const X& x)
> {
> std::cout << "Calling check_cast(const X&)...";
> return check_cast_impl<T, X, IsPtr<X>::value>::cast(x);
> }
>
[...]
> Consider, however, the following case where const is involved:
>
> class Base {
> public:
> virtual ~Base(){}
> };
>
> class Der: public Base {};
>
> const Base *pb = new Der;
> const Der& rd = check_cast<const Der&>(*pb);
This is a known issue with the partial ordering rules, f(T &) and f(T
const &) cannot be ordered. See
http://groups.google.com/groups?selm=3B0EEF21%40MailAndNews.com
and the associated thread.
This makes it really difficult to write functions that take anything.
f(T &) by itself fails for non-const rvalues, and the combined
approach fails for const l- or rvalues. Even if you don't care about
the const-ness, f(T const &) fails for functions (although there is a
defect report against it.)
I call this "the argument forwarding problem."
--
Peter Dimov
Multi Media Ltd.
---
[ 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.research.att.com/~austern/csc/faq.html ]