
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