$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
Subject: Re: [boost] is_convertible problems associated with move-only types
From: Howard Hinnant (hinnant_at_[hidden])
Date: 2009-01-06 11:28:18
On Jan 6, 2009, at 10:45 AM, David Abrahams wrote:
>
> on Mon Jan 05 2009, Howard Hinnant <hinnant-AT-twcny.rr.com> wrote:
>
>> I'm continuing to refine a boost::unique_ptr emulation and testsuite.
>
> Howard, can you put this in the sandbox?  I know there are a number of
> us who would like very much to collaborate on it.
I will make it available as soon as I can at http://home.roadrunner.com/~hinnant/unique_ptr03.html
There will be plenty of room for collaboration.  Indeed I feel like  
that is already happening with the discussion in this thread.
>> One thing I've just uncovered is that I'm hitting a brick wall with
>> is_convertible and move-only deleters used within the unique_ptr move
>> constructor.  The problem is that boost::is_convertible<From, To>
>> assumes From is an lvalue.
>
> Sorry to be nitpicky, but that's not making sense to me.  IIUC the  
> terms
> lvalue and rvalue can only apply to expressions, not types.  Are you
> saying that From has to be a reference type?  Surely not.
I'm saying that if you construct a To from a From, you /may/ get  
different answers (on whether the construction works or not) depending  
upon if you consider From an lvalue or rvalue:
 From f;
To t1 = f;  // one answer
To t2 = From();  // potentially a different answer
More specifically, if To and From are the same move-only type (say  
unique_ptr<int>), then you /do/ get a different answer.
That being said, the vast majority of the is_convertible use cases  
simply don't care if the source is considered an lvalue or rvalue.   
E.g.:
    is_convertible<T*, U*>::value
(same answer either way with pointers)
>> is_convertible<Deleter, Deleter> gets  instantiated and it is all  
>> over.
>
> Specifically, how?
The boost::is_convertible implementation requires access to a Deleter  
copy constructor.  My modifications to the boost::is_convertible  
implementation treat the From as an rvalue and will attempt the  
"auto_ptr solution" for making a copy from an rvalue when From is an  
emulated move-only type (avoiding the Deleter copy constructor, and  
using the conversion to the rv<Deleter> instead).
>> I've modified my copy of is_convertible (without really knowing  
>> what I'm doing) like
>> so:
>>
>> Index: is_convertible.hpp
>> ===================================================================
>> --- is_convertible.hpp	(revision 50433)
>> +++ is_convertible.hpp	(working copy)
>> @@ -119,6 +119,8 @@
>> struct any_conversion
>> {
>>     template <typename T> any_conversion(const volatile T&);
>> +    template <typename T> any_conversion(volatile T&);
>> +    template <typename T> any_conversion(const T&);
>>     template <typename T> any_conversion(T&);
>> };
>>
>> @@ -131,8 +133,8 @@
>> 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) )
>> +    static From _m_from();
>> +    static bool const value =  
>> sizeof( detail::checker<To>::_m_check(_m_from(), 0) )
>>         == sizeof(::boost::type_traits::yes_type);
>> };
>
> Note the From above is not the same as the one below at least in the
> current code.
I ran svn update on the boost-trunk before creating this diff.
>> @@ -291,7 +293,8 @@
>> template <typename From, typename To>
>> struct is_convertible_impl
>> {
>> -    typedef typename add_reference<From>::type ref_type;
>> +//    typedef typename add_reference<From>::type ref_type;
>> +    typedef From ref_type;
>>     BOOST_STATIC_CONSTANT(bool, value =
>>         (::boost::type_traits::ice_and<
>>             ::boost::type_traits::ice_or<
>
> So it used to pass an lvalue of type _m_from, and after your change it
> passes an rvalue.  Of course, your change now assumes that From can be
> returned from a function.  I think you'll have a problem if you try  
> this
> with From being a noncopyable non-movable type.
>
>
>> This hits just the part targeting gcc.
>
> (and old Borland compilers).
>
>> The intent is that in boost::is_convertible<From, To> From is now
>> considered an rvalue (unless From is a (lvalue) reference type).   
>> This
>> makes my test case happy.  I can now move construct a unique_ptr with
>> a move-only deleter.
>>
>> Fwiw, this definition of is_convertible is consistent with  
>> std::is_convertible in
>> N2800:
>
> I was just going to ask that.
To emulate the N2800 definition in C++03 one must treat void, array  
and function types specially in the implementation (arrays and  
functions can't be returned from functions and void can't be a  
function parameter).  Even so, a C++03 library emulation will get  
noncopyable non-movable types wrong (as will the current  
boost::is_convertible).  It will report a compile time error instead  
of false.  However C++03 /can/ get noncopyable movable types right,  
but only if From is considered an rvalue.  If the client specifically  
wants to consider From an lvalue, he can:
    is_convertible<From&, To>::value
This is not a newly discovered problem with is_convertible and move- 
only types:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2028.html#rel
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2157.html#rel
-Howard