Boost logo

Boost :

Subject: Re: [boost] is_convertible problems associated with move-only types
From: Howard Hinnant (hinnant_at_[hidden])
Date: 2009-01-07 11:20:06


On Jan 7, 2009, at 5:09 AM, John Maddock wrote:

> Howard,
>
> I believe there are a number of issues with the approach you're using:
>
> The trivial case is that if From is not default constructible then
> you're code won't compile.

I just reapplied my modifications to boost::is_convertible and
successfully compiled this:

#include <boost/type_traits.hpp>

class non_default_constructible
{
     int data_;

     non_default_constructible() : data_(0) {}
public:

     non_default_constructible(const non_default_constructible&) {}
     non_default_constructible& operator=(const
non_default_constructible&)
         {return *this;}

};

int main()
{
     bool b = boost::is_convertible<non_default_constructible,
                                    non_default_constructible>::value;
}

> More seriously there are some types which is_convertible currently
> works with that *can never be rvalues*, examples include function
> types and abstract classes: the latter in particular was the subject
> of several bug reports until we "fixed" it with the current
> implementation. Basically I think we're kind of painted into a
> corner here implementation wise :-(

I knew about functions but had neglected abstract classes. functions
(and arrays and void) can be handled by special cases detected with
is_function, is_array, is_void.

E.g. when From is an array type the only thing it is convertible to is
a compatible decayed pointer type. When From is a function type the
only thing that it is convertible to is a decayed pointer to itself
(not reference).

is_abstract can also be used to special case abstract classes, but
what to do with it is indeed problematic. I suspect that for a C++03
emulation the only thing one could do is consider the source an lvalue
(for is_abstract<From>::value true remap it to answer for
is_convertible<From&, To>). When rvalue reference is available, one
can do better than this with:

@@ -131,12 +133,12 @@
template <typename From, typename To>
struct is_convertible_basic_impl
{
- static From _m_from;
- static bool const value =
sizeof( detail::checker<To>::_m_check(_m_from, 0) )
+#ifdef BOOST_HAS_RVALUE_REFS
+ static From&& _m_from();
+#else
+ static From _m_from();
+#endif
+ static bool const value =
sizeof( detail::checker<To>::_m_check(_m_from(), 0) )
         == sizeof(::boost::type_traits::yes_type);
};

I.e. in C++0X one can create rvalue expressions having abstract type.

But I agree with you: For a C++03 emulation of std::is_convertible, I
don't see a way to consider From an rvalue for abstract types (but I
do see ways for the emulation to handle rvalue From function and array
types).

> Also the example you give is is_convertible<Movable, Movable>, is
> this the real use case? I ask because any is_convertible<T, T>
> expression is basically useless in C++03: it wil either compile and
> return true, or not compile at all, so you may just as well replace
> the expression with "true" and let the code fail later if the type
> is not copyable/movable :-(

It is a degenerate case caused by the overloading of the unique_ptr
move constructor and converting constructor:

    unique_ptr(uniquePtr&& u);
    template <class U, class E> unique_ptr(unique_ptr<U, E>&& u);

In the C++03 emulation of the second signature, one needs to enable_if
on is_convertible<E, D>. This signature gets instantiated whenever
there is a move construction, whether source and target have identical
deleters or not (indeed the spec could probably just drop the first
signature and let the second handle that case).

Even if you were to special case the is_convertible<D,D> case, you
only push the problem out a little. You still need to handle
is_convertible<E, D> where E and D are different move-only types and
an rvalue (but not lvalue) E is convertible to D.

> So yes, I would *love* to fix this, I just don't see how at
> present... John.

If the boost::is_convertible is not fixable I can just emulate
std::is_convertible directly in the unique_ptr emulation code. I've
already resorted to that solution for boost::compressed_pair:

http://home.roadrunner.com/~hinnant/unique_ptr.hpp

I called it detail::unique_ptr_storage (probably not the best name,
but not part of the interface anyway). I needed a "compressed_pair"
that would handle move-only types. unique_ptr_storage does the move-
only dance, and just enough of the compressed_pair dance to satisfy
unique_ptr's needs.

For unique_ptr, is_convertible will never have to deal with abstract
types (or void, array or function types for that matter), so the
emulation could be specialized/simplified just for the unique_ptr use
case.

Thanks for looking into this.

-Howard


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk