g++ 和 clang++ - 删除重载转换运算符歧义获取的指针

g++ and clang++ - delete pointer acquired by overloaded conversion operator ambiguity

我试图 post 这段代码作为 this question 的答案,方法是制作这个指针包装器(替换原始指针)。这个想法是将 const 委托给它的指针,这样 filter 函数就不能修改值。

#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 和 14 编译它没有错误,但 GCC 和 Clang on Coliru 声称存在歧义。我原以为他们会选择非常量 std::vector::front 重载,然后选择 my_pointer::operator T* &,但没有。这是为什么?

delete表达式接受一个转换表达式作为参数,可以是const也可以不是。

vec.front()不是const,但必须先转换为delete的指针。所以候选人 const int*int* 都是可能的候选人;编译器无法选择你想要的。

最简单的方法是使用强制转换来解决选择。例如:

delete (int*)vec.front();

备注:当你使用get()函数而不是转换时它有效,因为规则不同。重载函数的选择基于参数和对象的类型,而不是 return 类型。这里的非 const 是 best viable 函数,因为 vec.front() 不是 const。

[expr.delete]/1:

The operand shall be of pointer to object type or of class type. If of class type, the operand is contextually implicitly converted (Clause [conv]) to a pointer to object type.

[conv]/5,强调我的:

Certain language constructs require conversion to a value having one of a specified set of types appropriate to the construct. An expression e of class type E appearing in such a context is said to be contextually implicitly converted to a specified type T and is well-formed if and only if e can be implicitly converted to a type T that is determined as follows: E is searched for non-explicit conversion functions whose return type is cv T or reference to cv T such that T is allowed by the context. There shall be exactly one such T.

在您的代码中,有两个这样的 Tint *const int *)。因此,在您进行重载解析之前,它的格式是错误的。


请注意,C++11 和 C++14 之间的这个区域有所不同。 C++11 [expr.delete]/1-2 表示

The operand shall have a pointer to object type, or a class type having a single non-explicit conversion function (12.3.2) to a pointer to object type. [...]

If the operand has a class type, the operand is converted to a pointer type by calling the above-mentioned conversion function, [...]

如果从字面上看,这将允许您的代码并始终调用 operator const int*() const,因为 int* & 是引用类型,而不是指向对象类型的指针。在实践中,实现也会像 operator int*&() 一样考虑到 "reference to pointer to object" 的转换函数,然后拒绝该代码,因为它有不止一个符合条件的非显式转换函数。