使用模板时如何从 std::vector 中删除元素?

Posted

技术标签:

【中文标题】使用模板时如何从 std::vector 中删除元素?【英文标题】:How to erase an element from std::vector when using templates? 【发布时间】:2016-03-31 15:00:10 【问题描述】:

我有一个相当简单的模板类,我将项目存储在一个向量中。但是,当我尝试擦除元素时出现以下错误:

C2678: binary '==': no operator found which takes a left-hand operand of type
      'TestComponent' (or there is no acceptable conversion)

这是我正在使用的代码:

#pragma once
#include <vector>

template<class T>
class ComponentManager 
    ComponentManager() ;
    ~ComponentManager() ;

    T* newComponent() 
        components.emplace_back(T());
        return &components.back();
    

    // This is the method I'm having trouble with
    void destroyComponent(T* t) 
        components.erase(std::remove(components.begin(), components.end(), *t), components.end());
    

private:
    std::vector<T> components;
;

是的,我知道这会导致指针无效等等。不用去那里。

【问题讨论】:

TestComponent 是否定义了 operator == @NathanOliver 它没有。我想尽量不要它,因为泛型类型几乎可以是任何东西。我可能只是制定了删除错误。 与手头的问题无关,不必写emplace_back(T()),直接写emplace_back()即可 @manabreak 为了删除某些内容,您需要检查它们是否相等。如果你想提供一种比较两个对象的方法,你需要使用remove_if 为什么使用原始指针而不是引用? 【参考方案1】:

如果您尝试通过指针擦除 ,您需要为此使用正确的算法。 std::remove 在元素之间进行相等比较。根据您的 cmets,您不希望这样做,因此您可能更喜欢 std::remove_if

void destroyComponent(T* t) 
    components.erase(
        std::remove_if(components.begin(), components.end(), [t](const T& comp) 
            return &comp == t;
        ),
        components.end()
    );


请注意,将指针保存到 vector 中并不是特别安全,因为插入向量可能会导致重新分配,这会使之前保存的所有指针无效。您可能需要考虑使用不同的容器(或者只使用vector&lt;T*&gt;,或者更好的是vector&lt;unique_ptr&lt;T&gt;&gt;)。

【讨论】:

谢谢,这正是我想要的!直到现在才知道remove_if。只要计时器让我接受。 :) @mana 底部的部分是重要的部分。下次将元素添加到向量中时,您退出的 T* 可能会失效。你的设计有缺陷。【参考方案2】:

std::remove 在给定的序列中搜索由开始和结束迭代器定义的给定值,指定为第三个参数。显然,值的类型必须实现相等比较运算符,以便std::remove 将序列中的值与给定值进行比较。

如错误消息所述,您的 TestComponent 类未实现 == 运算符。

【讨论】:

【参考方案3】:

首先,您的解决方案很危险:看起来您保留了存储在std::vector 中的对象指针,除非您提前预留足够的空间,否则在添加新元素时您将获得一个悬空指针。如果您保留了足够的空间,您可能应该通过指针而不是值来删除对象:

components.erase(std::remove_if(components.begin(), components.end(), [t]( const T &tc )  return &tc == t;  ), components.end());

除非您可以按值唯一标识对象,然后您需要为该类实现正确的operator==

我建议将TestComponentstd::unique_ptr 一起存储,这样您就不会遇到std::vector 重新分配内存的问题,并且您的remove 将按预期工作而无需实现operator==

template<class T>
class ComponentManager 
    ComponentManager() ;
    ~ComponentManager() ;

     template< class... Args >
     T *newComponent( Args...&& args ) 
        components.emplace_back(std::make_unique<T>(std::forward<Args>(args)...));
        return components.back().get();
    

    // This is the method I'm having trouble with
    void destroyComponent(T* t) 
        components.erase(std::remove(components.begin(), components.end(), t), components.end());
    

private:
    std::vector<std::unique_ptr<T>>> components;
;

【讨论】:

感谢您的建议,这只是我正在涉足的一些玩具代码。 :) @manabreak 并不重要,因为您在vector 中使用指向对象的指针,您的下一个问题将是:为什么当我使用该指针时我的程序会崩溃。 不,不是。我确保容量足以满足我的需求,并且不会调整大小。 @manabreak 恕我直言,糟糕的解决方案,迟早你会达到这个限制。您至少应该检查newComponent 代码中的保留大小以防止重新分配。 无论它是多么糟糕的解决方案,这都会严重偏离轨道并且与原始问题无关。

以上是关于使用模板时如何从 std::vector 中删除元素?的主要内容,如果未能解决你的问题,请参考以下文章

从 std::vector 中删除前 N 个元素

为啥从 std::vector 中随机删除比 std::list 快?

哪位大神给解释一下std::vector<int>arrayN 如何理解,如何使用。arrayN改如何理解,谢谢。

如何获取向量中元素的类型?

如何在 C++ 中专门化 std::vector 的模板模板参数

从 std::vector 中删除多个对象?