你能将常量 T*&& 绑定到 T* 类型的 xvalue 吗?

Can you bind a const T*&& to an xvalue of type T*?

提问人:Jan Schultke 提问时间:9/16/2023 最后编辑:Jan Schultke 更新时间:10/5/2023 访问量:267

问:

请考虑以下代码:(https://godbolt.org/z/8W699x6q6)

int* p;
const int*&& r = static_cast<int*&&>(p);

注意:const int*&& 是对指向 const int 的指针的右值引用。

Clang 编译它,并绑定到一个临时对象:r

p: .quad   0
r: .quad   _ZGR1r_ // r is a reference to a temporary object, otherwise this would be p

GCC 拒绝以下代码:

<source>:2:18: error: binding reference of type 'const int*&&' to 'int*' discards qualifiers
    2 | const int *&&r = static_cast<int*&&>(p);
      |                  ^~~~~~~~~~~~~~~~~~~~~~

就我个人而言,我认为 GCC 正确地实现了 CWG 2352[dcl.init.ref] p4 的更改,但我不相信我的解释是正确的。 这里哪个编译器是正确的?


注意:本问题中的示例灵感来自CWG 2018中提到的最后一行代码。

注意:如果允许将 const int*&& 绑定到 int*&&这将提供一个 const 正确性脚枪。这与将 int** 转换为 const int** 的问题相同。就我个人而言,我认为委员会不太可能希望允许这种引用绑定,但是,尽管有缺陷报告,但措辞可能仍然允许这样做。

C++ 语言-律师 临时对象 常量正确性 引用绑定

评论

0赞 MSalters 9/28/2023
澄清一下,是与 不同的类型。具体来说,它不是一个 ,你是专门问一个指向常量 T 的非常量指针 - 对吧?当然,您可以从 xvalue 初始化一个临时值,这是一个有效的转换。T const*T*T* constT const*T*
0赞 Jan Schultke 9/29/2023
是的,我问的是右值 ref 到指向常量 T 的指针的绑定。

答:

2赞 Eightfold 10/5/2023 #1

是的,GCC 在这里是正确的。[dcl.init.ref]#4 和 [dcl.init.ref]#5.3.1 中的措辞仅在 T1 和 T2 具有相似的限定分解时才适用,因为指向 T1 的 prvalue 指针可以转换为指向 T2 的指针。

在这里,它们各自尝试转换的顺序是 和 。如果 绑定到指针(又名 cv0,在比较 cv 签名时不考虑),则这些类型将是兼容的,因为它们将具有相似的序列。但相反,分解 Tj 与 T2 不匹配,并且由于 prvalue 转换的要求是 Tj 与 T2 相同,因此它们并不相似。这意味着转换序列格式不正确,因此绑定格式不正确。* pcv1, * U, [int]* pcv1, * const U, [int]const

它格式错误的原因是,使用左值引用,通过将 a 绑定到 a ,您将能够修改对象。T*const T*&const

4赞 Brian Bi 10/5/2023 #2

根据 [dcl.init.ref]/4 中给出的定义,

  • “指针”与“指针”的引用不兼容,因为不能转换为 。const intintint**const int**
  • “指针”与“指针”的引用相关,因为这两种类型相似。const intint

引用兼容性控制何时可以进行直接绑定。这种直接绑定必须尊重常量正确性:如果一个对象可以通过引用 non-const 来引用,那么 a 的值可以通过 写入,从而将其值复制到 中。因此,这种直接绑定是不允许的。但是,如果引用类型本身是 const(即引用类型是 ),则它与 [conv.qual]/3 下的引用兼容。(Bullet 3.3 需要额外的 。int*prconst int*const int*rpconst int* constint*const

如果存在必要的引用兼容关系,则此引用绑定将由 [dcl.init.ref]/5.3 控制。在本例中,由于引用的类型与初始值设定项的类型不兼容,因此我们转到 [dcl.init.ref]/5.4.2,这需要创建一个类型的临时对象;引用绑定到该对象,而不是初始值设定项。const int*

我同意OP的观点,即这一结果似乎是无意的。CWG2018将以下间接引用绑定称为“奇怪”:

  • 引用绑定到const int* constint*
  • 引用绑定到const int*int*

前者由CWG2352直接引用绑定。后者不能是直接绑定,因为这种直接绑定会违反 const 正确性,因此它应该是格式错误的。

使此引用绑定格式不正确的另一个理由是:

const int x = 0;
int&& r = std::move(x);

这不会复制和绑定对临时的引用;它格式不正确。在这种情况下,不允许创建临时的原因是引用的类型与初始值设定项的类型“太接近”了:也就是说,这两种类型是引用相关的。引用相关性导致 p5.4.3 启动:x

CV1 应与 CV2 相同或更高 CV 资格;[...]

CWG2352将参考相关性扩展到 OP 给出的案例。看来,起草过程中的疏忽导致了目前的不一致。

新创建的CWG2801提出了纠正该问题的措辞,使 OP 的代码格式不正确。