Topic: Paradox in SFINAE with is_convertible
Author: Richard Smith <richard@ex-parrot.com>
Date: Wed, 5 Sep 2007 14:00:53 CST Raw View
Consider the following class (into which I have put some debugging
statements).
struct C {
C() { printf( "C::C()\n" ); }
template <class T>
C( T const&,
typename disable_if< is_convertible<T, C> >::type* = 0 )
{ printf( "C::C(T const&)\n" ); }
};
For the sake of argument, disable_if and is_convertible can be the
ones from boost. On the face of it, this is requesting that C be
implicitly constructible from T (i.e. that T be implicitly convertible
to C) if and only if T is not convertible to C. Put like that, this
is a classic example of a paradox, although it obviously doesn't
manifest until that constructor's declaration is instantiated. It's
easy enough to trigger the "paradox", though:
struct X {
operator C () const { return C(); }
};
void foo(C const&) {}
template <class T> T bar() { return T(); }
int main() {
foo(bar<X>());
foo(bar<int>());
}
Should this compile, and if so, what should this print out? Or is it
undefined behaviour or implementation defined?
I think this probably depends on how exactly is_convertible is
implemented, and most is_convertible implementations work something
like this:
template <class From, class To>
class is_convertible {
typedef char no;
typedef char (&yes)[2];
static no fn( ... );
static yes fn( To const& );
static From const& make();
public:
static const bool value = sizeof( fn( make() ) ) == sizeof(yes);
};
If a complete self-contained piece of code is wanted, we can take
disable_if to be defined as follows:
template <bool>
struct disable_if_c {};
template <>
struct disable_if_c<false> {
typedef void* type;
};
template <class T>
struct disable_if : disable_if_c<T::value> {};
. So, what happens?
My attempt to work this through the standard gets into an infinite
loop with overload resolution of is_convertible<X,C>::fn needing to
instantiate the declaration of C::C<X>( const X &, /*...*/ ) which in
turn requires overload resolution of is_convertible<X,C>::fn to be
complete. Is this caught by 14.7.1/14 that states that "the result of
an infinite recursion in instantiation is undefined."
If not, what does the standard say about the it?
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: greghe@pacbell.net (Greg Herlihy)
Date: Mon, 10 Sep 2007 03:42:10 GMT Raw View
On 9/5/07 1:00 PM, in article
1189007826.628060.40440@22g2000hsm.googlegroups.com, "Richard Smith"
<richard@ex-parrot.com> wrote:
> Consider the following class (into which I have put some debugging
> statements).
>
> struct C {
> C() { printf( "C::C()\n" ); }
>
> template <class T>
> C( T const&,
> typename disable_if< is_convertible<T, C> >::type* = 0 )
> { printf( "C::C(T const&)\n" ); }
> };
>
> For the sake of argument, disable_if and is_convertible can be the
> ones from boost. On the face of it, this is requesting that C be
> implicitly constructible from T (i.e. that T be implicitly convertible
> to C) if and only if T is not convertible to C.
The is_convertible class template requires that both of its type arguments
be complete types (which makes sense because the full "convertiblity" of a
type can be evaluated only after all of its member functions, including its
constructors, have been declared). In this case, the "C" type parameter used
in the is_convertible<> template has an incomplete type (appearing as it
does in the parameter list of one of its own member functions). Therefore
the outcome of evaluating is_convertible<T, C> is not defined by the draft
C++ Standard.
Greg
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: Richard Smith <richard@ex-parrot.com>
Date: Thu, 13 Sep 2007 11:48:58 CST Raw View
On Sep 10, 4:42 am, gre...@pacbell.net (Greg Herlihy) wrote:
> On 9/5/07 1:00 PM, in article
> 1189007826.628060.40...@22g2000hsm.googlegroups.com, "Richard Smith"
>
>
>
> <rich...@ex-parrot.com> wrote:
> > Consider the following class (into which I have put some debugging
> > statements).
>
> > struct C {
> > C() { printf( "C::C()\n" ); }
>
> > template <class T>
> > C( T const&,
> > typename disable_if< is_convertible<T, C> >::type* = 0 )
> > { printf( "C::C(T const&)\n" ); }
> > };
>
> > For the sake of argument, disable_if and is_convertible can be the
> > ones from boost. On the face of it, this is requesting that C be
> > implicitly constructible from T (i.e. that T be implicitly convertible
> > to C) if and only if T is not convertible to C.
>
> The is_convertible class template requires that both of its type arguments
> be complete types (which makes sense because the full "convertiblity" of a
> type can be evaluated only after all of its member functions, including its
> constructors, have been declared). In this case, the "C" type parameter used
> in the is_convertible<> template has an incomplete type (appearing as it
> does in the parameter list of one of its own member functions). Therefore
> the outcome of evaluating is_convertible<T, C> is not defined by the draft
> C++ Standard.
All true, but it's also missing my point.
The original post contained an example implementation of
is_convertible (which might not have been 100% compatible with the
draft standard, but that's not relevant either). Aside from needing
to #include <cstdio> for printf, it was a complete, compilable piece
of code, and it suffered from this 'paradox'. What should it print?
Now, so far as I can see, the fact that C isn't complete when
is_convertible<T, C> doesn't make the code undefined (unless by a
infinite template recursion argument such as I mentioned in my
original post).
--
Richard Smith
---
[ 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.comeaucomputing.com/csc/faq.html ]