为什么 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
。
所以更具体地说,我问以下问题:
is_convertible
真的抓住了可兑换性的直觉吗?
- 如果没有,它还捕获了什么?或者换句话说:我对可兑换性的直觉不合适吗?
- 如果我们把
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;",因此当您有用户定义的从 Foo
到 Bar
以及从 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 conversion 将 int const&
隐式转换为 int
(在非 class 类型上它会删除 cv 限定)。
我们还可以通过身份转换将 int
隐式转换为 int&&
(不需要转换,因为我们可以执行纯右值到右值引用的引用绑定)。
但是我们不能隐式地将 int const&
转换为 int&&
因为那需要
- 左值到右值的转换(Lvalue Transformation),
int const&
到int
,接着是
- 身份转换(引用绑定),
int
到int 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] 定义)
考虑以下静态断言:
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
。
所以更具体地说,我问以下问题:
is_convertible
真的抓住了可兑换性的直觉吗?- 如果没有,它还捕获了什么?或者换句话说:我对可兑换性的直觉不合适吗?
- 如果我们把
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;",因此当您有用户定义的从 Foo
到 Bar
以及从 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 conversion 将 int const&
隐式转换为 int
(在非 class 类型上它会删除 cv 限定)。
我们还可以通过身份转换将 int
隐式转换为 int&&
(不需要转换,因为我们可以执行纯右值到右值引用的引用绑定)。
但是我们不能隐式地将 int const&
转换为 int&&
因为那需要
- 左值到右值的转换(Lvalue Transformation),
int const&
到int
,接着是 - 身份转换(引用绑定),
int
到int 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] 定义)