Alexander Bily
Alexander Bily

Reputation: 965

Is std::is_convertible co-variant or contra-variant?

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

Answers (2)

einpoklum
einpoklum

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

R Sahu
R Sahu

Reputation: 206567

both test< Base*, Derived* >() and test< 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* > and is_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

Related Questions