Boost logo

Boost :

Subject: Re: [boost] is_convertible problems associated with move-only types
From: David Abrahams (dave_at_[hidden])
Date: 2009-01-06 12:37:03


on Tue Jan 06 2009, Howard Hinnant <hinnant-AT-twcny.rr.com> wrote:

> 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.

For optimal collaboration you need source control. That's why I suggest
the sandbox. That's probably the only way I'd be able to afford to work
on it.

>>> 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

Right.

> 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.

You miss my point. I'm just pointing out that in the current code, the
>From above is always a reference type and the From below is not
necessarily a reference type.

>>> @@ -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.

Did you overlook this remark completely?

>>> 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).

I'm claiming that your emulation gives a compile-time error in that
case.

-- 
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

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