【问题标题】:Object slicing and implicit type conversion对象切片和隐式类型转换
【发布时间】:2012-11-29 06:12:42
【问题描述】:

这是我正在使用的代码的简化版本:

namespace BasketNovel {

void Engine::BuryEntities()
{
    std::list<Entity*>::iterator iter = p_entities.begin();
    while (iter != p_entities.end())
    {
        if ( (*iter)->getAlive() == false )
        {
            delete (*iter);
            iter = p_entities.erase( iter ); //.erase returns next element
        }
        else iter++;
    }
}
}

我收到来自英特尔静态分析的以下警告:

BasketNovel.cpp(567):警告 #12221:在调用“std::_List_iterator”时对对象“iter”进行切片作为实际参数 2 > > std::list >::erase(std::_List_const_iterator > > )" 由于隐式类型转换而发生

我相信这基本上是说我正在导致隐式类型转换:

iter = p_entities.erase(iter);

(注意:即使我将代码更改为:p_entities.erase(iter++);,我也会收到相同的警告)

我不太明白我在上面的“切片”是什么。 这到底是什么意思,我应该如何解决这个警告?我宁愿稍微复杂的代码也不愿完全关闭警告消息。

【问题讨论】:

  • p_entities 是如何声明的?
  • 你用的是什么编译器?
  • @VaughnCato std::list p_entities;
  • 是否支持C++11?因为std::list::erase的签名变了。
  • @juanchopanza 是的,正如你所推断的那样。我已经开启了 C++11 支持。

标签: c++ type-conversion


【解决方案1】:

什么是对象切片

对象切片是复制/移动对象的一部分的事实,这通常发生在基础/派生对中:

struct Base { int i; };

struct Derived: Base { int j; };

void slice() {
    Derived d = {};

    Base b(d); // b is a "sliced" version of `d`
}

并且可能导致肮脏。

这里不过,这只是误报...

可以更简单吗?

是的,当然。

// Place to be deleted values at the end
auto const it = std::partition(p_entities.begin(), p_entities.end(),
                    [](Entity const* e) { return not e or not e->getAlive(); });

// Delete them
std::for_each(it, p_entities.end(), [](Entity const* e) { delete e; });

// Remove them
p_entities.erase(it, p_entities.end());

【讨论】:

  • 很好的回复,真的让我对 C++11 的使用大开眼界。但是我在最终的 p_entities.erase 中遇到了同样的错误。编译器告诉我我正在拆分“它”并进行隐式类型转换。顺便说一句,误报是什么意思?
  • @dk123: false positive 表示编译器错误地分析这是一个缺陷。它经常发生并带有警告:如果编译器确定这将是一个错误,但由于分析不完美,它只是一个警告,表明某些事情可能会出错..
  • 感谢您的定义。尽管您认为有什么可行的方法可以完全规避警告吗? (不只是关掉它)
  • @dk123: 可能是static_cast&lt;const_iterator&gt;(it) 表示这是故意的?不幸的是,我从未使用过 icc 我没有更好的 :(
  • @dk123:我刚刚意识到英特尔可能欺骗了iteratorconst_iterator 继承来免费获得自动转换,并且只实现了许多常用功能。这可以解释拼接警告:(
【解决方案2】:

看起来您的std::list::erase() 方法需要std::list&lt;Entity*&gt;::const_iterator,而您传递给它的是std::list&lt;Entity*&gt;::iterator。这可能意味着您正在编译支持 C++11 的代码。

一种解决方案是分两步执行删除。首先,使用std::for_each 删除并设置为0 指向不活动对象的指针。

#include <algorithm>

void deleteDead(Entity* e) { 
  if (e->getAlive()) return; 
  delete e;
  e = 0;
}
std::for_each(p_entities.begin(), p_entities.end(), deleteDead);

其次,使用 [erase-remove idiom](erase-remove idiom 删除 0 的元素。

#include <algorithm>
p_entities.erase(std::remove(p_entities.begin(), p_entities.end(), 0), 
                 p_entities.end() );

【讨论】:

  • 我刚刚尝试将语句更改为:iter = p_entities.erase(static_cast<:list>::const_iterator>(iter));但我仍然遇到同样的错误。有什么建议吗?
  • 感谢您的回复。我已经尝试过修改,但我在擦除时遇到了同样的错误:“std::_List_iterator<:_list_val> > > std:: list<:entity> >::erase(std::_List_const_iterator<:_list_val> > >,std::_List_const_iterator<:_list_val> > >)" 由于隐式类型转换而发生
  • 不要! 只有当您可以安全地丢弃这些值时,擦除删除习语才有效。在这里,OP 需要先删除它们。
  • @MatthieuM。谢谢,我错过了删除。我提供了另一种解决方案,但我发现它与您发布的非常相似。
  • @juanchopanza:我们可以争辩说 OP 应该使用 std::unique_ptr 然后擦除删除成语将干净地应用。
【解决方案3】:

在做了大约一个月的其他工作后,我意识到问题的答案基本上在于改变

std::list::迭代器

std::list::const_iterator

发生切片是因为 .erase() 需要一个 const_iterator 并从迭代器进行隐式转换。

我建议在标题中使用 typedef std::list 以涵盖未来可能的类型更改。

我保留 MatthieuM. 的答案,因为我认为对象切片的定义比这个答案本身有用得多。

【讨论】:

    猜你喜欢
    • 2021-04-13
    • 2010-10-14
    • 2013-06-30
    • 2011-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-27
    • 2013-07-09
    相关资源
    最近更新 更多