【问题标题】:Const correctness causing problems with containers for pointers?常量正确性导致指针容器出现问题?
【发布时间】:2014-01-03 03:24:36
【问题描述】:

鉴于此代码(使用 C++、Qt 容器,但我想问题是普遍的):

// a containter for Item-s
QList<Item*> items;

// argument is const to prevent changing the item by this function
void doStuff(const Item *item)
{
    // find index of the item inside the container
    // indexOf() is declared as:
    // template <typename T> int QList<T>::indexOf(const T &t, int from = 0) const
    const int itemIndex = items->indexOf(item);
}

我得到一个编译错误(MSVC2010):

错误 C2664:“QList::indexOf”:无法将参数 1 从“const Item *”转换为“Item *const &”

[
T=项目 *
]
转换失去限定符

我认为由于indexOf() 是使用const T &amp; 参数声明的,因此该参数将成为const Item* &amp;(对指向常量项的指针的引用),这很容易从const Item* 参数中获得。不幸的是,由于const T&amp; t and T const &amp;t are equivalent,由于某种原因,编译器似乎将参数视为Item* const &amp;t,它读作“对一个项目的const指针的引用”,这是另一回事,不会使Item指向不可变的。

我的解释正确吗?为什么即使函数以一种不会改变参数的方式声明,编译器也会把事情搞砸?这真的是 const 语法等价如何搞砸事情的一个例子吗?为什么编译器使用后一种形式而不是前一种形式?如果我想在容器中存储指针并保持严格的 const 语义,我该怎么办?

【问题讨论】:

  • 试试items-&gt;indexOf(*item);
  • @MadPhysicist: 不行,那个参数是const Item 类型,也不能转换成Item *const &amp;
  • 好吧,您的 QList 是 Item* 的列表,而不是 const Item* 的列表。你能逃脱QList&lt;const Item*&gt; 吗?请记住 T*const T*T* constconst T* const 都是非常不同的东西
  • 我认为这是指针语义与泛型编程相结合的产物。 const Item*(也写成Item const*)和Item* const是两种不同的类型; Item const* 无法转换为 Item* const。它也是泛型编程的产物,因为您可以Item* constItem const* 进行比较,但QList 的接口似乎不支持这一点(C++1y 将提供支持使用通用比较器 à la std::less&lt;&gt; 用于标准库容器)。
  • @neuviemeporte 事实上,std::find(items.constBegin(), items.constEnd(), item) 应该可以解决问题,因为它不假定 T 的类型与取消引用的迭代器的类型相同。标准库容器目前在指针的 const 正确性方面存在“同样的问题”,尽管它们没有那么多可能有问题的 member 函数。

标签: c++ pointers const-correctness container-data-type


【解决方案1】:

在这种情况下,您可以使用const_cast 删除const-ness 而不会违反您的功能保证。

// argument is const to prevent changing the item by this function
void doStuff(const Item *item)
{
    // find index of the item inside the container
    // indexOf() is declared as:
    // template <typename T> int QList<T>::indexOf(const T &t, int from = 0) const
    const int itemIndex = items->indexOf(const_cast<Item*>(item));
}

那是因为indexOf 只是在容器中找到指针,而不是取消引用指针并改变另一边的内容。

【讨论】:

  • 没有别的办法了吗?我的意思是, const_cast 看起来如此……矫枉过正?为什么即使函数承诺不触及参数,编译器也会搞砸?
  • @neuviemeporte : 谢天谢地,你必须抛弃const 的地方很少。但是,存在的少数是不可避免的。 (另一个地方是 free() 函数,尽管在 C 中实现 const 正确性时这个问题会更加突出。)
  • @neuviemeporte :我认为这是 C++11 应该通过完美转发来清理的情况之一。原则上,应该可以安全地将T*const T* 进行比较,但是indexOf 引用包含的类型这一事实意味着您无法尝试在@ 中发送reference to const T*需要 987654332@。不过,我的“右值引用”理解非常非常有限,所以请原谅我的含糊之处。有没有完美转发专家愿意提供帮助?
  • 我不认为完美的转发可以帮助解决这个问题。它应该减少必要的副本数量(有利于移动),并 forward 参数表达式的 l/rvalueness。 (但不自称是专家。)
  • @DyP :我认为有了 C++11 的一些特性,我什至还不能自称是新手。 ;-)
猜你喜欢
  • 1970-01-01
  • 2020-07-22
  • 2012-02-07
  • 1970-01-01
  • 2011-06-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多