G ++ and clang ++ - delete a pointer obtained due to the ambiguity of an overloaded conversion operator

I tried to publish this code as an answer to this question by making this pointer wrapper (replacing the raw pointer). The idea is to delegate const to your pointer so that the filter function cannot change values.

 #include <iostream> #include <vector> template <typename T> class my_pointer { T *ptr_; public: my_pointer(T *ptr = nullptr) : ptr_(ptr) {} operator T* &() { return ptr_; } operator T const*() const { return ptr_; } }; std::vector<my_pointer<int>> filter(std::vector<my_pointer<int>> const& vec) { //*vec.front() = 5; // this is supposed to be an error by requirement return {}; } int main() { std::vector<my_pointer<int>> vec = {new int(0)}; filter(vec); delete vec.front(); // ambiguity with g++ and clang++ } 

Visual C ++ 12 and 14 will compile this without errors, but GCC and Clang on Coliru argue that there is ambiguity. I expected them to select the non-const std::vector::front overload, and then my_pointer::operator T* & , but not. Why?

+5
source share
2 answers

[expr.delete] / 1:

The operand must have a pointer to an object type or class type. If the type is a class, the operand is implicitly converted to a context (point [conv]) by a pointer to the type of object.

[conv] / 5, my emphasis is:

Some language constructs require conversion to a value that has one specific set of types that correspond to the construct. an expression e type e that appears in such a context for context-indirect conversion to a given type T and is correctly formed if and only if e can be implicitly converted to type T , which is defined as follows: e searches for an implicit conversion function, type returning cv T or a reference to cv T such that T resolved by context. There must be exactly one such T

There are two such T in your code ( int * and const int * ). Therefore, it is poorly formed before you even go to resolution.


Note that in this area there are changes between C ++ 11 and C ++ 14. C ++ 11 [expr.delete] / 1-2 says

The operand must have a pointer to an object type or class type with one implicit conversion function (12.3.2) to an object type pointer. [...]

If the operand is of type class, the operand is converted to a pointer type, calling the above conversion function, [...]

What if you read literally, resolve your code and always call operator const int*() const , because int* & is a reference type, not a pointer to an object type. In practice, implementations look at conversion functions for a "reference to an object pointer", for example, operator int*&() , and then reject the code because it has more than one qualifying implicit conversion function.

+8
source

In the delete expression, the expression takes the expression as an argument, which can be const or not.

vec.front() not const, but it must first be converted to a pointer to delete . Thus, both candidates const int* and int* are possible candidates; the compiler cannot choose which one you want.

The best thing to do is use the throw to decide the choice. For instance:

 delete (int*)vec.front(); 

Note: It works when you use the get() function instead of conversion, because the rules are different. The choice of an overloaded function is based on the type of parameters and object, and not on the return type. Here non const is the best viable function since vec.front() not const.

+1
source

Source: https://habr.com/ru/post/1238715/


All Articles