给定 int **p1 和 const int**p2,p1 == p2 是合法的吗?

34

给定以下函数:

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

clang(回到版本3.0)会产生这个警告(现场查看):

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 Studio2013年)都不会产生警告,比较如下:

使用-pedantic-errors标志会使其成为错误。根据标准,gcc(返回至4.3.6版本)和Visual Studio(2013年版)都不会产生警告,比较的内容是:

p1 == p2

格式正确?

更一般地,如果两个多级指针除了第一级以外的cv-qualifications不同,使用等号运算符或关系运算符进行比较是否格式正确?


非常有用的知识,已点赞。希望 MSVC 和 G++ 也能跟进实现 C++14。 - Cheers and hth. - Alf
1
如果gcc和VS允许此代码而没有警告或错误,则它们已经遵循了C++14。clang需要修复。@Cheersandhth.-Alf - M.M
@MattMcNabb:谢谢。我的脑海里有一些二进制切换。 - Cheers and hth. - Alf
@Cheersandhth.-Alf在我的答案中添加了一个来自五年前的clang提交,表明这是被clang、gcc和EDG视为扩展,我认为VS也是如此。请注意,在会议上,它被接受并指出这一变化只是将现有实践编码化。 - Shafik Yaghmour
1个回答

24
在C++14之前,这种情况是不规范的,更一般的带有一些例外情况也是不规范的。这在缺陷报告1512:指针比较与限定转换中有所涵盖,其中提到:

According to 5.9 [expr.rel] paragraph 2, describing pointer comparisons,

Pointer conversions (4.10 [conv.ptr]) and qualification conversions (4.4 [conv.qual]) are performed on pointer operands (or on a pointer operand and a null pointer constant, or on two null pointer constants, at least one of which is non-integral) to bring them to their composite pointer type.

This would appear to make the following example ill-formed,

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

because int** cannot be converted to const int**, according to the rules of 4.4 [conv.qual] paragraph 4. This seems too strict for pointer comparison, and current implementations accept the example.

这份缺陷报告指出,尽管它格式不正确,但实现仍接受这样的比较。这个clang commit表明它被视为扩展,并且表明gccEDG也将其视为扩展,据推测Visual Studio也是如此。
这在标准中已得到解决,N3624: Core Issue 1512: Pointer comparison vs qualification conversions提到:

This paper presents the modifications to the Working Draft necessary to resolve core issues 583 and 1512. In particular, it makes

[...]

and

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

well-formed.

请注意,在会议中已经接受的情况下,已经注意到这只是将现有做法编码。
在标准的其他更改中,该段落被添加到第5节的结尾[expr],其中包括新术语cv-combined type

The cv-combined type of two types T1 and T2 is a type T3 similar to T1 whose cv-qualification signature (4.4) is:

  • for every j > 0, cv3,j is the union of cv1,j and cv2,j ;
  • if the resulting cv3,j is different from cv1,j or cv2,j , then const is added to every cv3,k for 0 < k < j.

[ Note: Given similar types T1 and T2, this construction ensures that both can be converted to T3. —end note ] The composite pointer type of two operands p1 and p2 having types T1 and T2, respectively, where at least one is a pointer or pointer to member type or std::nullptr_t, is:

  • if both p1 and p2 are null pointer constants, std::nullptr_t;
  • if either p1 or p2 is a null pointer constant, T2 or T1, respectively;
  • if T1 or T2 is “pointer to cv1 void” and the other type is “pointer to cv2 T”, “pointer to cv12 void”, where cv12 is the union of cv1 and cv2 ;
  • if T1 is “pointer to cv1 C1” and T2 is “pointer to cv2 C2”, where C1 is reference-related to C2 or C2 is reference-related to C1 (8.5.3), the cv-combined type of T1 and T2 or the cv-combined type of T2 and T1, respectively;
  • if T1 is “pointer to member of C1 of type cv1 U1” and T2 is “pointer to member of C2 of type cv2 U2” where C1 is reference-related to C2 or C2 is reference-related to C1 (8.5.3), the cv-combined type of T2 and T1 or the cv-combined type of T1 and T2, respectively;
  • if T1 and T2 are similar multi-level mixed pointer and pointer to member types (4.4), the cv-combined type of T1 and T2;
  • otherwise, a program that necessitates the determination of a composite pointer type is ill-formed.

[ Example:

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

The composite pointer type of p and q is “pointer to const void”; the composite pointer type of pi and pci is “pointer to const pointer to const int”. —end example ]


1
在什么情况下,一个 int** 和一个 const int** 可以在没有 reinterpret_cast 的情况下相等? - user253751
1
@immibis,严格别名规则指出编译器可以假设 *x 不与 *y 别名(显然对于 **x**y 不成立),因此我怀疑这个测试可以被优化为 false(一旦允许的话!) - M.M
2
@MattMcNabb 不行,它做不到。严格别名允许通过“与对象的动态类型类似(如4.4中定义)的类型”进行访问。const int *int *相似的 - T.C.
2
@immibis 简单的答案是“如果两个都是空指针”。无论如何,将 int**const int** 之间进行转换只需要使用 const_cast,而不是 reinterpret_cast,我认为在所有通常和预期的方式中使用结果指针是有效的。 - user743382

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接