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

时间:2019-04-15 12:43:17

标签: c++ type-conversion typetraits type-systems

考虑以下静态断言:

    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&>为假。我也希望能得到右值引用。

我认为它与is_convertible的定义有关。在定义中,To自变量作为虚函数的返回类型出现。据我了解,任何类型的新值都是临时的,因此可以转换为右值引用。因此std::is_convertible_v<T, T&&>适用于任何类型的T

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

  1. is_convertible是否真的抓住了可兑换的直觉?
  2. 如果没有,它还能捕获什么?或用不同的措辞:我的可兑换性直觉不合适吗?
  3. 如果我们将is_convertible理解为一种二元关系,那它不应该是一个预定单,即和物吗?为什么不呢?

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

尤其是,T不应转换为T&&,因为在需要T的地方,您不能使用T&&(您可能无法从{{1} },但是您可以从T移出。

我在这里遇到严重问题吗?

2 个答案:

答案 0 :(得分:3)

  

直觉上,恕我直言,可兑换性是指:每当需要输入To时,您也可以使用From ....类型。

这就是它的意思。

  

...这将意味着可传递性。

不,这是不正确的。并非每个二进制关系都必须是可传递的。从cppreferene开始隐式转换:

  

隐式转换序列按以下顺序组成:

     

1)零或一个标准转换序列;

     

2)零或一次用户定义的转换;

     

3)零个或一个标准转换序列。

     

在考虑构造函数或用户定义的参数时   转换功能,仅允许一个标准转换序列   (否则,可以有效地链接用户定义的转化)。   从一种内置类型转换为另一种内置类型时,仅   允许一个标准的转换顺序。

确切的规则涉及其中,但是考虑“零或一个用户定义的转换”;因此,当用户定义了从FooBar以及从BarBaz的转换时,这并不一定意味着Foo会转换为{{ 1}}!

并不是Baz拥有可转换的怪异概念,但是C ++中关于可转换内容的规则并不是一开始就可以传递的。

答案 1 :(得分:1)

  

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

通常来说,您不想将const T&转换为T&&。这可能会带来灾难性的影响。您可能希望发生编译器错误,以免意外std::move从调用者那里获取数据(或者创建一个看起来像没有副本的 一样的意外副本)


现在,标准对此要说些什么? http://localhost..

  

标准转换顺序是以下顺序的标准转换顺序:
   -下列一组中的零或一转换:左值到右值转换,数组到指针转换以及函数到指针转换。
   -以下集合中的零个或一个转换:整数提升,浮点提升,整数转换,浮点转换,浮点积分转换,指针转换,指针到成员转换以及布尔转换。
   -零或一个函数指针转换。
   -零或一次资格转换。

因此,我们可以通过[conv]int const&隐式转换为int(在非类类型上,它将删除cv资格)。

我们还可以通过Identity会话将int隐式转换为int&&(无需转换,因为我们可以将prvalue进行引用绑定到rvalue引用)。

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

  • 从左值到右值转换(左值转换),从int const&int,然后
  • 身份转换(引用绑定),从intint const&

这是因为根据lvalue-to-rvalue conversion,我们无法将Identity转换与其他转换混合使用:

  

标准转换序列要么是身份转换本身(即没有转换),要么由其他四个类别中的一到三个转换组成。

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