Boost logo

Boost :

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


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