[type traits] possible bug in boost::is_convertible for types with private copy constructors

From the move semantics thread:
2008/6/24 David Abrahams <dave@boostpro.com>:
Daniel James wrote:
It mostly works. I've found two problems so far. Firstly, is_convertible didn't work for the type,
You found a bug in is_convertible?
I don't know if it's a bug. The example below fails to compile. #include <boost/type_traits/is_convertible.hpp> template <typename T> struct move_from {}; class Y { Y(Y& rhs); public: Y(move_from<Y>) {} }; bool const r = boost::is_convertible<move_from<Y>, Y>::value;

On Jun 24, 2008, at 7:07 AM, Daniel James wrote:
From the move semantics thread:
2008/6/24 David Abrahams <dave@boostpro.com>:
Daniel James wrote:
It mostly works. I've found two problems so far. Firstly, is_convertible didn't work for the type,
You found a bug in is_convertible?
I don't know if it's a bug. The example below fails to compile.
#include <boost/type_traits/is_convertible.hpp>
template <typename T> struct move_from {}; class Y { Y(Y& rhs); public: Y(move_from<Y>) {} };
bool const r = boost::is_convertible<move_from<Y>, Y>::value;
The tr1::is_convertible, which was born from boost::is_convertible, considers its "from" to be an lvalue when detecting convertibility. The C++0X std::is_convertible considers its "from" to be an rvalue when detecting convertibility. If one wants to restrict is_convertible to only consider lvalue froms, one simply specifies an lvalue reference: is_convertible<F&, T>::value (from is now considered as an lvalue). There is motivation for this change here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2255.html (search for "is_convertible"). -Howard

Howard Hinnant <hinnant <at> twcny.rr.com> writes:
The tr1::is_convertible, which was born from boost::is_convertible, considers its "from" to be an lvalue when detecting convertibility. The C++0X std::is_convertible considers its "from" to be an rvalue when detecting convertibility. If one wants to restrict is_convertible to only consider lvalue froms, one simply specifies an lvalue reference: is_convertible<F&, T>::value (from is now considered as an lvalue).
There is motivation for this change here:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2255.html
(search for "is_convertible").
Very interesting! But now the question is, how do we resolve this (and other issues like it that will surely come up) for Boost? -- Dave Abrahams Boostpro Computing http://www.boostpro.com

On Jun 26, 2008, at 9:19 AM, David Abrahams wrote:
Howard Hinnant <hinnant <at> twcny.rr.com> writes:
The tr1::is_convertible, which was born from boost::is_convertible, considers its "from" to be an lvalue when detecting convertibility. The C++0X std::is_convertible considers its "from" to be an rvalue when detecting convertibility. If one wants to restrict is_convertible to only consider lvalue froms, one simply specifies an lvalue reference: is_convertible<F&, T>::value (from is now considered as an lvalue).
There is motivation for this change here:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2255.html
(search for "is_convertible").
Very interesting! But now the question is, how do we resolve this (and other issues like it that will surely come up) for Boost?
I don't think there is any one answer. Sometimes namespaces are the answer. boost::X need not be the same thing as std::tr1::X which need not be the same thing as std::X. In this particular case, I would expect a vanishingly small amount of code to be broken by changing from the boost::is_convertible spec to the std::is_convertible spec. If something is convertible from an lvalue, then it is usually convertible from an rvalue. Obviously this statement can be refuted with an example such as: struct A { A(B&); }; But I am asserting that the above code is sufficiently rare, especially when combined with the use of is_convertible. For most uses of is_convertible, it does not matter whether the source is considered an lvalue or an rvalue (pointers probably make up most of the use cases). For those remaining cases where it does matter, most of those want to consider the source an rvalue (move-only types). And in a context where the "from" is a perfectly forwarded template, [temp.deduct.call]/3 assures that is_convertible considers the "from" with the same l/r-valueness as the argument being passed to the deduced template: struct B {}; struct A { A(B&); }; template <class T> typename enable_if < is_convertible<T, A>::value, void
::type foo(T&&);
void test() { B b; foo(b); // ok, even with A(B&) foo(B()); // foo not found, A(B()) not allowed } -Howard

Howard Hinnant wrote:
On Jun 26, 2008, at 9:19 AM, David Abrahams wrote:
Very interesting! But now the question is, how do we resolve this (and other issues like it that will surely come up) for Boost?
I don't think there is any one answer. Sometimes namespaces are the answer. boost::X need not be the same thing as std::tr1::X which need not be the same thing as std::X.
But boost::tr1::x had better match match std::tr1::x
In this particular case, I would expect a vanishingly small amount of code to be broken by changing from the boost::is_convertible spec to the std::is_convertible spec. If something is convertible from an lvalue, then it is usually convertible from an rvalue. Obviously this statement can be refuted with an example such as:
struct A { A(B&); };
But I am asserting that the above code is sufficiently rare, especially when combined with the use of is_convertible.
For most uses of is_convertible, it does not matter whether the source is considered an lvalue or an rvalue (pointers probably make up most of the use cases). For those remaining cases where it does matter, most of those want to consider the source an rvalue (move-only types). And in a context where the "from" is a perfectly forwarded template, [temp.deduct.call]/3 assures that is_convertible considers the "from" with the same l/r-valueness as the argument being passed to the deduced template:
At least one thing is clear: is_convertible shouldn't ever cause a compile-time error. If one of the possible implementations always avoids a compile-time error, I vote for that one. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Jun 26, 2008, at 12:33 PM, David Abrahams wrote:
At least one thing is clear: is_convertible shouldn't ever cause a compile-time error. If one of the possible implementations always avoids a compile-time error, I vote for that one.
I believe the only time you can get a compile-time error out of is_convertible is if you use it with types which fail to meet these requirements:
From and To shall be complete types, arrays of unknown bound, or (possibly cv-qualified) void types.
-Howard

Howard Hinnant wrote:
On Jun 26, 2008, at 12:33 PM, David Abrahams wrote:
At least one thing is clear: is_convertible shouldn't ever cause a compile-time error. If one of the possible implementations always avoids a compile-time error, I vote for that one.
I believe the only time you can get a compile-time error out of is_convertible is if you use it with types which fail to meet these requirements:
From and To shall be complete types, arrays of unknown bound, or (possibly cv-qualified) void types.
Just look back at the first message of this thread for a counterexample. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Jun 26, 2008, at 1:30 PM, David Abrahams wrote:
Howard Hinnant wrote:
On Jun 26, 2008, at 12:33 PM, David Abrahams wrote:
At least one thing is clear: is_convertible shouldn't ever cause a compile-time error. If one of the possible implementations always avoids a compile-time error, I vote for that one.
I believe the only time you can get a compile-time error out of is_convertible is if you use it with types which fail to meet these requirements:
From and To shall be complete types, arrays of unknown bound, or (possibly cv-qualified) void types.
<chuckle> Touché. :-) std::is_convertible is supposed to get access correct. The only way we currently know how to do that is with compiler help. Library implementations, no matter whether they treat From as an lvalue or rvalue, are apparently doomed to fail to compile when there is an access violation. -Howard
participants (3)
-
Daniel James
-
David Abrahams
-
Howard Hinnant