为什么 C++ 中的类型可转换性不可传递?

Why is type convertibility in C++ not transitive?

考虑以下静态断言:

    static_assert(std::is_convertible_v<int const&, int const>);
    static_assert(std::is_convertible_v<int const, int>);
    static_assert(std::is_convertible_v<int, int &&>);

    static_assert(std::is_convertible_v<int const&, int &&>);

以上三个assert通过,最后一个assert失败

这意味着 C++ 中的类型可转换性一般来说是不可传递的,我认为这是非常违反直觉的。

我搜索了标准和 cppreference 网站以找到任何证据表明这是有意的行为,但到目前为止我还没有成功。

有趣的是,对于左值引用,一切都很好,因为 std::is_convertible_v<int, int&> 是假的。我也希望右值引用。

我认为这与 is_convertible 的定义方式有关。在定义中,To 参数显示为 return 类型的虚函数。根据我的理解,任何类型的新值都是临时的,因此可以转换为右值引用。因此 std::is_convertible_v<T, T&&> 适用于任何类型 T

所以更具体地说,我问以下问题:

  1. is_convertible真的抓住了可兑换性的直觉吗?
  2. 如果没有,它还捕获了什么?或者换句话说:我对可兑换性的直觉不合适吗?
  3. 如果我们把is_convertible理解为二元关系,不应该是先序即传递吗?为什么不呢?

直觉上,恕我直言,可转换性应该意味着:每当需要类型 To 时,您也可以使用类型 From。这意味着传递性。

特别是,T 不应转换为 T&&,因为您不能在需要 T&& 的地方使用 T(您不能从 [=例如 15=],但您可以从 T&&).

我这里有严重错误吗?

Intuitively, imho, convertiblity should mean: whenever a type To is required, you can also use type From....

这就是它的意思。

...And this would imply transitivity.

不,这是不正确的。并非每个二元关系都必须具有传递性。来自 cppreferene 隐式转换:

Implicit conversion sequence consists of the following, in this order:

1) zero or one standard conversion sequence;

2) zero or one user-defined conversion;

3) zero or one standard conversion sequence.

When considering the argument to a constructor or to a user-defined conversion function, only one standard conversion sequence is allowed (otherwise user-defined conversions could be effectively chained). When converting from one built-in type to another built-in type, only one standard conversion sequence is allowed.

确切的规则相当复杂,但请考虑 "zero or one user-defined conversion;",因此当您有用户定义的从 FooBar 以及从 Bar 到 [=13= 的转换时] 那么这并不一定意味着 Foo 转换为 Baz!

不是 std::is_convertible 有一个奇怪的可转换概念,而是 C++ 中关于什么是可转换的规则从一开始就不是可传递的。

This means that type convertibility in C++ is not transitive in general, which I think is very counterintuitive.

一般来说,您不想将 const T& 转换为 T&&。这可能会产生灾难性的后果。你会想要一个编译器错误,这样你就不会不小心 std::move 来自他们的调用者的数据(或者,创建一个 看起来 就像没有复制的意外副本)


现在,标准对此有何规定? [conv]

A standard conversion sequence is a sequence of standard conversions in the following order:
  - Zero or one conversion from the following set: lvalue-to-rvalue conversion, array-to-pointer conversion, and function-to-pointer conversion.
 - Zero or one conversion from the following set: integral promotions, floating-point promotion, integral conversions, floating-point conversions, floating-integral conversions, pointer conversions, pointer-to-member conversions, and boolean conversions.
 - Zero or one function pointer conversion.
 - Zero or one qualification conversion.

因此,我们可以通过 lvalue-to-rvalue conversionint const& 隐式转换为 int(在非 class 类型上它会删除 cv 限定)。

我们还可以通过身份转换将 int 隐式转换为 int&&(不需要转换,因为我们可以执行纯右值到右值引用的引用绑定)。

但是我们不能隐式地将 int const& 转换为 int&& 因为那需要

  • 左值到右值的转换(Lvalue Transformation),int const&int,接着是
  • 身份转换(引用绑定),intint const&

这是因为根据 [over.ics.scs]:

,我们不能以那种方式将身份转换与其他转换混合使用

a standard conversion sequence either is the Identity conversion by itself (that is, no conversion) or consists of one to three conversions from the other four categories.

(类别由 table in [over.ics.scs] 定义)