我在我的程序中使用 QVector 保存指向对象的指针,比如 FYPE*。
class TYPE {
// ....
};
const TYPE *ptrToCst;
QVector<TYPE*> qtVct;
// ...
if (qtVct.contains(ptrToCst)) { // Error!!!
// ....
}
编译器说 QVector::contains 期望 TYPE* 而不是 const TYPE* 作为它的参数。 const_cast 操作可以解决这个问题。但这对我来说没有任何意义,因为 contains 方法永远不应该改变指针指向的内容。使用 STL vector 的等效代码按预期工作。
std::vector<TYPE*> stlVct;
// ...
if (std::find(stlVct.begin(), stlVct.end(), ptrToCst)) { // Ok
// ...
}
造成这种差异的原因是什么? STL 是否特别对待保存指针的容器,以便 std::find 接受指向 const 对象的指针?我猜涉及到部分模板特化?
最佳答案
这实际上是一个关于常量正确性和一些非直觉错误的非常有趣的问题,但编译器拒绝代码是正确的。
为什么在 Qt 中不起作用?
编译器拒绝该代码,因为它会破坏代码的常量正确性。我不知道图书馆,但我想我可以安全地假设 contains 的签名是 QVector<T>::contains( T const & value )
[1],在 T == type*
的情况下意思是:
QVector<type *>::contains( type * const & value )
现在的问题是您正在尝试传递类型为 type const *
的变量.此时的问题是编译器不知道什么contains
在内部,只有它在界面中提供的 promise 。 contains
promise 不会更改指针,但对指针只字不提。签名中没有任何内容禁止执行 contains
从修改值。考虑这个类似的例子:
void f( int * const & p ) { // I promise not to change p
*p = 5; // ... but I can modify *p
}
int main() {
const int k = 10;
int const * p = &k;
f( p ); // Same problem as above: if allowed, f can modify k!!!
}
为什么它允许对 std::find
的类似调用那么呢?
与std::find
的区别是那个find
是一个模板,其中不同的参数类型具有非常松散的耦合。标准库不对接口(interface)执行类型检查,而是对模板的实现执行类型检查。如果参数的类型不正确,将在实例化模板时选取它。
这意味着实现将类似于:
template <typename Iterator, typename T>
Iterator find( Iterator start, Iterator end, T const & value ) {
while ( start != end && *start != value )
++start;
return start;
}
迭代器的类型和值是完全无关的,除了模板内的代码强加的约束之外,没有其他约束。这意味着调用会将参数匹配为 Iterator == std::vector<type*>::iterator
和 T = type const *
, 并且签名将匹配。因为内部值仅用于与 *start
进行比较,以及 type *
的比较和 type const * const
是有效的(它将前者转换为后者,然后比较 [2])代码完美编译。
如果模板有额外的限制,即第二个参数的类型是 Iterator::value_type const &
(这可以用 SFINAE 实现)然后 find
将无法编译并出现相同的错误。
[1] 注意声明中顺序的选择:type const *
, 而不是 const type *
.对于编译器(和有经验的人)来说,它们完全相同,但是总是添加 const
在右侧,识别什么 被定义为const 并识别const T &
变得微不足道。与 T == type *
在 contains
的论点中是不是 const type *&
.
[2] 与您无法绑定(bind) type * const &
的方式相同到 type const *
,你不能用指针做同样的事情,type * const *
无法从 type const *
转换而来, 但是如果const
被添加到指针和指针类型,那么转换就很好,因为它保证不会破坏 const 的正确性。该转换(这是安全的)是在 type * == type const * const
的比较中执行的, 左边有两个额外的 const
: type const * const
.如果不清楚为什么这不会破坏常量正确性,请发表评论,我将在此处提供一些代码。
关于c++ - 为什么 QVector<TYPE*>::contains 期望指向非常量 TYPE 的指针作为其参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8005080/