Differences between std::is_convertible and std::convertible_to (in practice)?
Asked Answered
U

1

41

According to en.cppreference.com (from what I can gather):

  • std::is_convertible is a trait class requiring types From & To to be such that a function with return type To that returns a From value can compile.
  • std::convertible_to is a concept requiring types From & To to be as explained above, AND such that an r-value reference of type From can be converted with static_cast<To>.

The requirement imposed by std::is_convertible seems relatively straight-forward. Conversely, the r-value reference casting requirement of std::convertible_to seems oddly specific for such a generic concept that is shown in simple examples for C++20 features.

Being a novice in C++, I could not quite understand some terminology and parts of the supplementary descriptions provided in both webpages and I cannot imagine the exact difference between the requirements of either.

Some inter-related questions:

  • What are the practical implications for types From & To of not only being constrained by std::is_convertible but also by the strange r-value reference casting requirement?
  • What kind of candidate types for From & To are additionally rejected by the r-value reference casting requirement?
  • Why might a programmer want to use either of std::is_convertible or std::convertible_to, instead of the other, as constraints for their function return types or parameter types (aside from just the convenience of concepts)?

A simpler explanation or an example would help. Thank you!

Unrig answered 29/6, 2020 at 17:58 Comment(0)
Z
42

std::is_convertible<From, To> (the type trait) checks is type From is implicitly convertible to type To.

std::convertible_to<From, To> (the concept) checks that From is both implicitly and explicitly convertible to To. It's rare that this is not the case, such types are ridiculous, but it's nice in generic code to just not have to worry about that case.

One example:

struct From;
struct To {
    explicit To(From) = delete;
};
struct From {
    operator To();
};

static_assert(std::is_convertible_v<From, To>);
static_assert(not std::convertible_to<From, To>);
Zeus answered 29/6, 2020 at 18:2 Comment(5)
Agreed! I'd like to add that it can be important to consider a custom requires clause or concept instead (that, as an example, might make use of just std::Is_convertible instead) when you know the exact requirements you need and can thus allow for more candidate types to be accepted without using a catch-all concept like convertible_to!Unrig
"checks that type From is implicitly convertible to type to" is a category error: types aren't convertible to types in C++ - despite that the library specification says so all over the place - expressions are convertible to types. is_convertible_v<From, To> and is_convertible<From, To> both actually check that an expression e such that decltype((e)) is From is convertible to type To.Birkenhead
+100 for "it's nice in generic code to just not have to worry about that case." - much of the point of the standard library concepts is to make it clear that we consider cases like this pathological by excluding them formally.Birkenhead
-0.1 for using not instead of !Trinatrinal
@Birkenhead add_rvalue_reference_t<From> :) We don't care about prvalue nonsense other than void... (Edit: actually, we want remove_cv_t<add_rvalue_reference_t<From>> - LWG issue submitted)Cinerarium

© 2022 - 2024 — McMap. All rights reserved.