请考虑以下静态断言:
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 &&>);
以上三个断言通过,但最后一个断言失败。
这意味着C ++中的类型可转换性一般不具有传递性,我认为这是非常违反直觉的。
我搜索了标准和cppreference网站,找到任何证据表明这是预期的行为,但到目前为止我还没有成功。
有趣的是,对于左值引用,一切都很好,因为std::is_convertible_v<int, int&>
是假的。我也希望rvalue-references。
我认为它与如何定义is_convertible
有关。在定义中,To
参数显示为虚函数的返回类型。根据我的理解,任何类型的新值都是临时的,因此可以转换为右值引用。因此std::is_convertible_v<T, T&&>
适用于任何类型的T
。
更具体地说,我提出以下问题:
is_convertible
真的抓住了可兑换的直觉吗?is_convertible
理解为二元关系,那么它不应该是预订,即传递吗?为什么不?直观地说,imho,convertiblity应该意味着:只要需要类型To
,你也可以使用类型From
。这意味着传递性。
特别是,T
不应该转换为T&&
,因为你不能使用需要T
的T&&
(你可能不会从T
移动,但你可能会从T&&
移动)。
我在这里遇到了严重的错误吗?
直观地说,imho,convertiblity应该意味着:只要需要类型To,你也可以使用类型From ....
这就是它的意思。
......这意味着传递性。
不,这不正确。并非每个二元关系都必须是可传递的。来自cppreferene的隐式转换:
隐式转换序列按以下顺序包含以下内容:
1)零个或一个标准转换序列;
2)零或一个用户定义的转换;
3)零个或一个标准转换序列。
在考虑构造函数或用户定义的转换函数的参数时,只允许一个标准转换序列(否则可以有效地链接用户定义的转换)。从一种内置类型转换为另一种内置类型时,只允许一个标准转换序列。
确切的规则是相关的,但考虑“零或一个用户定义的转换;”因此,当你有用户定义的从Foo
到Bar
以及从Bar
到Baz
的转换时,这并不一定意味着Foo
转换为Baz
!
它不是std::is_convertible
有一个奇怪的可转换概念,但C ++中有关可兑换的规则从一开始就不具有传递性。
这意味着C ++中的类型可转换性一般不具有传递性,我认为这是非常违反直觉的。
一般来说,你不想将const T&
转换为T&&
。这可能会带来灾难性的后果。你想要一个编译器错误,这样你就不会意外地从调用者那里获取调用者的数据(或者,创建一个看似没有复制的非预期副本)
现在,标准对此有何看法? std::move
标准转换序列是一系列标准转换,顺序如下: - 来自以下集合的零或一次转换:左值到右值的转换,数组到指针的转换以及函数到指针的转换。 - 来自以下集合的零或一次转换:整数促销,浮点促销,积分转换,浮点转换,浮点积分转换,指针转换,指针到成员转换和布尔转换。 - 零或一个函数指针转换。 - 零或一个资格转换。
因此,我们可以通过[conv]隐式地将int const&
转换为int
(在非类型类型上它会删除cv资格)。
我们还可以通过Identity转换隐式地将lvalue-to-rvalue conversion转换为int
(不需要转换,因为我们可以执行prvalue与rvalue引用的引用绑定)。
但我们不能隐含地将int&&
转换为int const&
,因为这需要
int&&
到int const&
,然后是int
到int
这是因为我们不能以这种方式将身份转换与其他转换混合,根据int const&
:
标准转换序列要么是身份转换本身(即没有转换),要么包含来自其他四个类别的一到三次转换。
(由[over.ics.scs]定义的类别)