Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Given int **p1 and const int**p2 is p1 == p2 well formed?

Given the following function:

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

clang(back to version 3.0) produces this warning (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) { }       ~~ ^ ~~ 

Using -pedantic-errors flags turns it into an error. Neither gcc(back to 4.3.6) nor Visual Studio(2013) produce a warning, according to the standard, is the comparison:

p1 == p2 

well formed?

More generally, if two multi-level pointers differ in their cv-qualifications other than at the first level is comparison via the equality operator or relational operators well-formed?

like image 329
Shafik Yaghmour Avatar asked Mar 12 '15 19:03

Shafik Yaghmour


1 Answers

Before C++14 this case was ill-formed and the more general case with some exceptions was also ill-formed. This is covered in defect report 1512: Pointer comparison vs qualification conversions , which says:

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.

The defect report points out although this was ill-formed, implementations accepted such comparisons. This clang commit indicates it was treated as an extension and indicates both gcc and EDG also treats this as an extension, presumably this is also the case for Visual Studio.

This was resolved in the standard by N3624: Core Issue 1512: Pointer comparison vs qualification conversions, which says:

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.

Also note that in the meeting it was accepted it was noted that this just codified existing practice.

Amongst other changes to the standard, this paragraph was added to the end of section 5 [expr], which includes the new term 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 ]

like image 171
Shafik Yaghmour Avatar answered Oct 06 '22 04:10

Shafik Yaghmour