【问题标题】:Given int **p1 and const int**p2 is p1 == p2 well formed?给定 int **p1 和 const int**p2 p1 == p2 格式正确吗?
【发布时间】:2015-05-15 03:25:34
【问题描述】:

给定以下函数:

void g(int **p1, const int**p2)
{
   if (p1 == p2) { }  
}

clang(回到版本 3.0) 产生这个警告 (see it live):

warning: comparison of distinct pointer types ('int **' and 'const int **')
uses non-standard composite pointer type 'const int *const *' 
[-Wcompare-distinct-pointer-types]
  if (p1 == p2) { }
      ~~ ^ ~~

使用-pedantic-errors 标志会将其变为错误。 gcc(回到 4.3.6) 和 Visual Studio(2013) 都不会产生警告,根据标准,是比较:

p1 == p2

结构良好?

更一般地,如果两个多级指针的 cv-qualifications 不同,而不是在第一级是通过相等运算符或格式良好的关系运算符进行比较?

【问题讨论】:

  • 知道非常有用,点赞。希望 msvc 和 g++ 在实现 c++14 时效仿。
  • @Cheersandhth.-如果 Alf gcc 和 VS 在没有警告或错误的情况下允许此代码,则它们已经遵循 C++14;铿锵声需要修复
  • @MattMcNabb:谢谢。我脑子里有一些二进制切换。
  • @Cheersandhth.-Alf 在我的回答中添加了五年前的 clang 提交,表明这被 clang、gcc 和 EDG 视为扩展,我也认为是 VS。会议上注意到它被接受,它表明这改变只是编纂了现有的做法。

标签: c++ c++11 language-lawyer c++14


【解决方案1】:

在 C++14 之前,这种情况是不合格式的,更一般的情况(有一些例外)也是不合格式的。 defect report 1512: Pointer comparison vs qualification conversions 对此进行了介绍,其中写道:

根据 5.9 [expr.rel] 第 2 段,描述指针 比较,

指针转换 (4.10 [conv.ptr]) 和限定转换 (4.4 [conv.qual]) 对指针操作数(或 一个指针操作数和一个空指针常量,或者两个空指针 常数,其中至少一个是非整数的)将它们带到 它们的复合指针类型。

这似乎会使以下示例不正确,

bool foo(int** x, const int** y) {
   return x < y;  // valid ?
}

因为 int** 不能转换为 const int**,根据 4.4 [conv.qual] 第 4 段的规则。这似乎对 指针比较,目前的实现都接受这个例子。

缺陷报告指出,尽管这是不正确的,但实现接受了这样的比较。这个clang commit 表示它被视为扩展,并且表明gccEDG 也将其视为扩展,大概Visual Studio 也是这种情况。

N3624: Core Issue 1512: Pointer comparison vs qualification conversions 在标准中解决了这个问题,其中说:

本文介绍了对工作草案的必要修改 解决核心问题 583 和 1512。特别是,它使

[...]

void g(int **p1, const int**p2)
{
   if (p1 == p2) { ... }
}

结构良好。

另请注意,在meeting it was accepted 中指出,这只是对现有做法的编纂。

在对标准的其他更改中,这一段被添加到5 部分的末尾[expr],其中包括新的术语cv-combined type

两种类型T1和T2的cv-combined type是T3和T1相似的类型 其 cv-qualification 签名 (4.4) 是:

  • 对于每个 j > 0,cv3,j 是 cv1,j 和 cv2,j 的并集;
  • 如果生成的 cv3,j 与 cv1,j 或 cv2,j 不同,则 const 将添加到每个 cv3,k 中,用于 0

[注:给定相似类型T1 和 T2,这种结构确保两者都可以转换为 T3。 —尾注] 两个操作数 p1 和 p2 的复合指针类型 分别具有 T1 和 T2 类型,其中至少一个是指针 或指向成员类型或 std::nullptr_t 的指针是:

  • 如果 p1 和 p2 都是空指针常量,std::nullptr_t;
  • 如果 p1 或 p2 是空指针常量,则分别为 T2 或 T1;
  • 如果 T1 或 T2 是“指向 cv1 void 的指针”,而另一个类型是“指向 cv2 T 的指针”,则“指向 cv12 void 的指针”,其中 cv12 是 cv1 和 简历2;
  • 如果 T1 是“指向 cv1 C1 的指针”并且 T2 是“指向 cv2 C2 的指针”,其中 C1 与 C2 引用相关或 C2 与 C1 引用相关 (8.5.3), T1 和 T2 的 cv 组合类型或 T2 和 T2 的 cv 组合类型 分别为 T1;
  • 如果 T1 是“指向 cv1 U1 类型的 C1 成员的指针”并且 T2 是“指向 cv2 U2 类型的 C2 成员的指针”,其中 C1 与 C2 或 C2 与 C1 (8.5.3) 的引用相关,C1 是 T2 的 cv 组合类型,并且 T1 或 T1 和 T2 的 cv 组合型;
  • 如果T1和T2是类似的多级混合指针和指向成员类型的指针(4.4),T1和T2的cv-combined类型;
  • 否则,需要确定复合指针类型的程序是不正确的。

[示例:

    typedef void *p;
    typedef const int *q;
    typedef int **pi;
    typedef const int **pci;

p 和 q 的复合指针类型是“指向 const void 的指针”;这 pi 和 pci 的复合指针类型是“指向 const 的指针 常量整数”。 ——结束示例]

【讨论】:

  • 在什么情况下int**const int** 在没有reinterpret_cast 的情况下实际上是相等的?
  • @immibis 严格的别名规则说编译器可以假设 *x 不别名 *y**x**y 显然不是这样),所以我怀疑这一点测试可以优化为false(一旦被允许!)
  • @MattMcNabb 不,它不能。严格的别名允许通过“与对象的动态类型类似(如 4.4 中定义)的类型”进行访问。 const int *int *相似
  • @immibis 简单的答案是“如果两者都是空指针”。无论如何,在int**const int** 之间进行转换只需要const_cast,而不是reinterpret_cast,我怀疑以所有通常和预期的方式使用结果指针是有效的。
猜你喜欢
  • 1970-01-01
  • 2013-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-26
  • 1970-01-01
  • 1970-01-01
  • 2011-12-06
相关资源
最近更新 更多