为什么QVector <type *> :: contains需要指向非const TYPE的指针作为参数?</type *>

时间:2011-11-04 05:05:06

标签: c++ qt pointers stl const

我正在使用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对象的指针?我想涉及部分模板专业化?

2 个答案:

答案 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*>::iteratorT = 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的比较中执行的,其中左侧有两个额外的consttype 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作为参数的指针”:因为你用模板参数告诉了它。