我正在使用QVector在我的程序中保存指向对象的指针,比如FYPE *。
class TYPE {
// ....
};
const TYPE *ptrToCst;
QVector<TYPE*> qtVct;
// ...
if (qtVct.contains(ptrToCst)) { // Error!!!
// ....
}
编译器说QVector :: contains期望TYPE *而不是const TYPE *作为参数。 const_cast操作可以解决这个问题。但它对我没有任何意义,因为contains方法永远不会改变指针指向的内容。使用STL向量的等效代码按预期工作。
std::vector<TYPE*> stlVct;
// ...
if (std::find(stlVct.begin(), stlVct.end(), ptrToCst)) { // Ok
// ...
}
造成这种差异的原因是什么? STL是否特别处理了持有指针的容器,以便std :: find接受指向const对象的指针?我想涉及部分模板专业化?
答案 0 :(得分:2)
这实际上是关于const-correctness和一些非直观错误的一个非常有趣的问题,但编译器拒绝代码是正确的。
为什么Qt不起作用?
编译器拒绝代码,因为它会破坏代码的const正确性。我不知道库,但我想我可以放心地假设contains的签名是QVector<T>::contains( T const & value )
[1] ,在T == type*
的情况下意味着:< / p>
QVector<type *>::contains( type * const & value )
现在问题是您正在尝试传递type const *
类型的变量。此时的问题是编译器不知道内部contains
做什么,只知道它在接口中提供的promise。 contains
承诺不会更改指针,但对指针一无所知。签名中没有任何内容阻止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
是一个模板,其中不同的参数类型具有非常松散的耦合。标准库不对接口执行类型检查,而是执行模板。如果参数不是正确的类型,则在实例化模板时将会选择。
这意味着实施将类似于:
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] 与您无法将type * const &
绑定到type const *
的方式相同,您无法使用指针执行等效操作,type * const *
不能如果将type const *
添加到指针和指针类型中,则转换自const
,但,然后转换就可以了,因为它保证不会破坏const正确性。该转化(这是安全的)是在type * == type const * const
的比较中执行的,其中左侧有两个额外的const
:type const * const
。如果不清楚为什么这不会破坏const-correctness,请删除注释,我将在这里提供一些代码。
答案 1 :(得分:1)
对具体类型进行显式模板实例化,您可以确定标准库供应商不知道您是否编写TYPE
。除此之外,区别在于签名。 std::find
是一个免费的功能模板,如:
template <typename I, typename T>
find(I first, I last, T value)
因此,当您调用它时,编译器会生成find(std::vector<TYPE*>::iterator, std::vector<TYPE*>::iterator, const TYPE*)
。由于所有find
都会进行比较,您可以毫无问题地比较const T*
和T*
,但一切都很好而且蓬松。
QVector<TYPE*>::contains
是类模板中的成员函数。因此它的签名包含用于实例化模板的类型:
contains(TYPE*)
其中存在问题,因为您尝试使用const TYPE*
调用它 - 并且const T*
转换为T*
是非法的。
另外:find
返回迭代器,而不是布尔值。您的情况应为if (std::find(...) != that_vector.end())
。
直接回答“为什么QVector :: contains需要指向非const TYPE作为参数的指针”:因为你用模板参数告诉了它。