Reputation: 965
According to cppreference, std::is_convertible< From, To >
should behave as if following imaginary function is well formed:
template < class To, class From >
To test() {
return std::declval< From >();
}
This implies, that std::is_convertible< From, To >::value
shall evaluate to true
if To
is covariant with From
(in other words, when From
is base class of To
, std::is_convertible< From, To >::value
should evaluate to true
).
Let's say that we have two classes.
struct Base {};
struct Derived : public Base {};
Now, if we call imaginary test method, both test< Base*, Derived* >()
and test< Derived*, Base* >()
will actually be well formed, because you can return pointer to derived class even though formal return type is pointer to base class. But result of is_convertible< Base*, Derived* >
and is_convertible< Derived*, Base* >
will differ (std::is_convertible_v< Base*, Derived* >
is equal to false. Is the description of this trait accurate?
EDIT
Here is my attempt to implement is_convertible
trait
template < class T, class U >
U convert() {
return std::declval< T >();
}
template < class T, class U, bool isTVoid = std::is_void_v< T >, bool isUVoid = std::is_void_v< U >, class = void >
struct is_convertible_helper
: public std::false_type {};
template < class T, class U >
struct is_convertible_helper< T, U, false, false, std::void_t< decltype(convert< T, U >()) > >
: public std::true_type {};
template < class T, class U >
struct is_convertible_helper< T, U, false, true, void >
: public std::false_type {};
template < class T, class U >
struct is_convertible_helper< T, U, true, false, void >
: public std::false_type {};
template < class T, class U >
struct is_convertible_helper< T, U, true, true, void >
: public std::true_type {};
template < class T, class U >
struct is_convertible
: public is_convertible_helper< T, U > {};
Reason why I asked this question was the fact, that both is_convertible< A*, B* >::value
and is_convertible< B*, A* >::value
evaluated to true
.
Upvotes: 1
Views: 591
Reputation: 131445
Conversion from type A to type B and a subtype relation between type A and type B are very distinct things.
While it is true that if A is a subtype of B then B is convertible to A (well, there might be slicing, and assuming copy ctors are available etc. but in practice you would convert a B& to an A&), the opposite direction is certainly not true. int
and double
are inter-convertible and are obviously not subtypes of each other.
So I think you're kind of off-base trying to relate these concepts - without going into details.
Upvotes: 0
Reputation: 206567
both
test< Base*, Derived* >()
andtest< Derived*, Base* >()
will actually be well formed.
Not true.
test<Derived*, Base*>()
is not well formed since Base*
cannot be converted to Derived*
.
See the problem at https://ideone.com/N73ROY.
But result of
is_convertible< Base*, Derived* >
andis_convertible< Derived*, Base* >
will differ (std::is_convertible_v< Base*, Derived* >
is equal to false)
That is the correct behavior.
Is the description of this trait accurate?
Yes.
Your confusion stemmed from the first statement. Since, that is not correct, the rest of the answers should make sense.
Upvotes: 3