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
.
在您的代码中,有两个这样的 T
(int *
和 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" 的转换函数,然后拒绝该代码,因为它有不止一个符合条件的非显式转换函数。
我试图 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 typeE
appearing in such a context is said to be contextually implicitly converted to a specified typeT
and is well-formed if and only if e can be implicitly converted to a typeT
that is determined as follows:E
is searched for non-explicit conversion functions whose return type iscv T
or reference tocv T
such thatT
is allowed by the context. There shall be exactly one suchT
.
在您的代码中,有两个这样的 T
(int *
和 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" 的转换函数,然后拒绝该代码,因为它有不止一个符合条件的非显式转换函数。