is_convertible problems associated with move-only types

I'm continuing to refine a boost::unique_ptr emulation and testsuite. 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. is_convertible<Deleter, Deleter> gets instantiated and it is all over. 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); }; @@ -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< This hits just the part targeting gcc. 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:
In order to instantiate the template is_convertible<From, To>, the following code shall be well formed:
template <class T> typename add_rvalue_reference<T>::type create(); To test() { return create<From>(); }
[ Note: This requirement gives well defined results for reference types, void types, array types, and function types. -- end note ]
I've got a much more refined unique_ptr testsuite on the way which will contain the unique_ptr header and test case for this (not there yet, I'll post when it is). -Howard

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:
In order to instantiate the template is_convertible<From, To>, the following code shall be well formed:
template <class T> typename add_rvalue_reference<T>::type create(); To test() { return create<From>(); }
[ Note: This requirement gives well defined results for reference types, void types, array types, and function types. -- end note ]
I've got a much more refined unique_ptr testsuite on the way which will contain the unique_ptr header and test case for this (not there yet, I'll post when it is).
Howard, have you checked that this doesn't break any of the existing type_traits tests? If not can you let me have one (or more!) test cases for the problem so I can work on patching is_convertible here? Thanks, John.

On Jan 6, 2009, at 4:53 AM, John Maddock wrote:
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:
In order to instantiate the template is_convertible<From, To>, the following code shall be well formed:
template <class T> typename add_rvalue_reference<T>::type create(); To test() { return create<From>(); }
[ Note: This requirement gives well defined results for reference types, void types, array types, and function types. -- end note ]
I've got a much more refined unique_ptr testsuite on the way which will contain the unique_ptr header and test case for this (not there yet, I'll post when it is).
Howard, have you checked that this doesn't break any of the existing type_traits tests? If not can you let me have one (or more!) test cases for the problem so I can work on patching is_convertible here?
Hi John, I haven't tested is_convertible at all, much less developed fixes for all the different #else branches. Here's a simplified test case: #include <boost/type_traits.hpp> #include <boost/static_assert.hpp> class move_only { int data_; move_only(move_only& u); move_only& operator=(volatile move_only&); class rv { move_only& r_; public: explicit rv(move_only& r) : r_(r) {} move_only* operator->() {return &r_;} }; public: operator rv() {return rv(*this);} move_only(rv r) : data_(r->data_) {} move_only& operator=(rv r) {data_ = r->data_; return *this;} friend move_only move(move_only& p) {return move_only(rv(p));} friend move_only move(rv r) {return move_only(r);} move_only() : data_(0) {} }; move_only source() {return move_only();} int main() { move_only x = source(); BOOST_STATIC_ASSERT((boost::is_convertible<move_only, move_only>::value)); } If it compiles, it is good. My best guess is that it will be DOA on VC ++ unless the /Za option is used. A VC++-no-/Za workaround for the move_only type is: #if !BROKEN move_only(move_only& u); move_only& operator=(volatile move_only&); #else public: move_only(move_only& u) : data_(u.data_) {} move_only& operator=(volatile move_only& u) {data_ = u.data_; return *this;} private: #endif Various people here at boost probably already have better VC++-no-/Za solutions. I don't have this compiler to experiment with. -Howard

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.
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.
is_convertible<Deleter, Deleter> gets instantiated and it is all over.
Specifically, how?
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.
@@ -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. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

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

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

On Jan 6, 2009, at 12:37 PM, David Abrahams wrote:
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.
Understood. I'm a boost sandbox newbie and am juggling too many things today to even learn how to get boost sandbox access. For now I have a better, but still incomplete, tests and source up at: http://home.roadrunner.com/~hinnant/unique_ptr03.html The tests are hierarchical, organized according to N2800's [unique.ptr]. One can: $ export CC=g++ $ export BOOST_INCLUDE="-I/<path-to>/boost-trunk" $ export SOURCE_INCLUDE="-I/<path-to>/unique_ptr" And then cd into any directory under unique.ptr and: $ ./test 6 of the tests currently fail for me. 2 of them are fixed by changing boost::is_convertible's From to an rvalue. The other 4 I currently don't know how to fix. They involve unique_ptr's converting ctor. My current fear is that our generalized move library won't handle this, but I hope I'm wrong. $ ./test ... failed 6 tests in /Users/hinnant/Development/unique_ptr/unique.ptr/ unique.ptr.single/unique.ptr.single.ctor ... failed 6 tests in /Users/hinnant/Development/unique_ptr/unique.ptr/ unique.ptr.single failed 6 tests in /Users/hinnant/Development/unique_ptr/unique.ptr
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.
I know that. I did that on purpose. I'm still missing your point.
@@ -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?
No, I commented on it below.
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.
I'm claiming that so does boost: Compiled against revision 50433: $ cat test.cpp #include <boost/type_traits.hpp> class non_copyable_non_movable { int data_; non_copyable_non_movable(const non_copyable_non_movable&); non_copyable_non_movable& operator=(const non_copyable_non_movable&); public: non_copyable_non_movable() : data_(0) {} }; int main() { bool b = boost::is_convertible<non_copyable_non_movable, non_copyable_non_movable>::value; } $ g++ g++ -I/Users/hinnant/Development/boost-dev/boost-trunk test.cpp /Users/hinnant/Development/boost-dev/boost-trunk/boost/type_traits/ is_convertible.hpp: In instantiation of ‘boost::detail::is_convertible_basic_impl<non_copyable_non_movable&, non_copyable_non_movable>’: /Users/hinnant/Development/boost-dev/boost-trunk/boost/type_traits/ is_convertible.hpp:295: instantiated from ‘boost::detail::is_convertible_impl<non_copyable_non_movable, non_copyable_non_movable>’ /Users/hinnant/Development/boost-dev/boost-trunk/boost/type_traits/ is_convertible.hpp:372: instantiated from ‘boost::detail::is_convertible_impl_dispatch<non_copyable_non_movable, non_copyable_non_movable>’ /Users/hinnant/Development/boost-dev/boost-trunk/boost/type_traits/ is_convertible.hpp:418: instantiated from ‘boost::is_convertible<non_copyable_non_movable, non_copyable_non_movable>’ test.cpp:18: instantiated from here test.cpp:7: error: ‘non_copyable_non_movable::non_copyable_non_movable(const non_copyable_non_movable&)’ is private /Users/hinnant/Development/boost-dev/boost-trunk/boost/type_traits/ is_convertible.hpp:136: error: within this context /Users/hinnant/Development/boost-dev/boost-trunk/boost/type_traits/ is_convertible.hpp:136: error: initializing argument 1 of ‘static boost::type_traits::yes_type boost::detail::checker<T>::_m_check(T, int) [with T = non_copyable_non_movable]’ I know of no way to fix this in C++03. I strongly suspect it will take C++0X std::Convertible to get this one right (or compiler support such as __is_convertible(T,U)). Correct handling of noncopyable nonmovable types has no bearing on this discussion. We are *not* going to get it right. However we *can* get noncopyable movable types right if we choose to. Currently boost handles only copyable types. -Howard

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. 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 :-( 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 :-( So yes, I would *love* to fix this, I just don't see how at present... John.

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

On Tue, 6 Jan 2009 13:12:27 -0500, Howard Hinnant <hinnant@twcny.rr.com> wrote:
I'm a boost sandbox newbie and am juggling too many things today to even learn how to get boost sandbox access.
You already have it. The sandbox is just an area in our svn repo to which you have read/write permission already. See http://svn.boost.org -- David Abrahams Boostpro Computing http://www.boostpro.com
participants (3)
-
David Abrahams
-
Howard Hinnant
-
John Maddock