C++ 用类的实例替换枚举。避免堆分配。

huangapple go评论89阅读模式
英文:

C++ Replace enum with instances of class. Avoid heap allocation

问题

我想用类的实例来替代这个枚举:
enum{ eTagA, eTagB,...}

这允许添加新的标签,而无需修改枚举。
我有以下代码:

#include <memory>
#include <list>

struct TagBase {
    bool operator== (const TagBase& other) const {
        return typeid(*this) == typeid(other);
    }

    bool operator!= (const TagBase& other) const {
        return !(*this == other);
    }
    
    virtual ~TagBase() = default;
};

bool operator== (const std::unique_ptr<TagBase>& lhs, const TagBase& base) {
    return base == *(lhs.get());
}

class Namespace {
public:
    template<typename T>
    void AddTag() {
        tags.push_back(std::unique_ptr<T>(new T()));
    }
    
    bool HasTag(const TagBase& tag) const {
        return std::find_if(tags.begin(), tags.end(), [&tag](const std::unique_ptr<TagBase>& ptr) {
            return *ptr == tag;
        }) != tags.end();
    }

private:
    // 使用 std::unique_ptr 是唯一的方式吗?
    std::list<std::unique_ptr<TagBase>> tags;
};

struct TagA: public TagBase {};
struct TagB: public TagBase {};
struct TagC: public TagBase {};

int main (int argc, const char* argv[])
{
    Namespace nm;
    
    nm.AddTag<TagA>();
    nm.AddTag<TagB>();
    
    assert(nm.HasTag(TagA{}));
    assert(nm.HasTag(TagB{}));
    assert(nm.HasTag(TagC{}) == false);
}

它可以工作,但我对以下几点有一些想法:

  1. 在这种情况下使用 "typeid" 是否适当?这是可移植的吗?
  2. 我不能在 "std:list" 中使用原始的 TagBase 类,因为会发生切片问题。我能否避免使用 "unique_ptr",因为它涉及堆分配?
  3. 也许有更好的模式来完成这个任务?

我还尝试过强烈使用模板(std::is_same 等),但没有得到合适的解决方案。

英文:

I want to replace this enum:
enum{ eTagA, eTagB,...}.

With instances of classes. This allows to append new tags, without modifying the enum.
I've got following code:

#include &lt;memory&gt;
#include &lt;list&gt;

struct TagBase {
    bool operator== (const TagBase&amp; other) const {
        return typeid(*this) == typeid(other);
    }

    bool operator!= (const TagBase&amp; other) const {
        return !(*this == other);
    }
    
    virtual ~TagBase() = default;
};

bool operator== (const std::unique_ptr&lt;TagBase&gt;&amp; lhs, const TagBase&amp; base) {
    return base == *(lhs.get());
}

class Namespace {
public:
    template&lt;typename T&gt;
    void AddTag() {
        tags.push_back(std::unique_ptr&lt;T&gt;(new T()));
    }
    
    bool HasTag(const TagBase&amp; tag) const {
        return std::find(std::begin(tags), std::end(tags), tag) != tags.end();
    }

private:
    // using of std::unique_ptr is the only way?
    std::list&lt;std::unique_ptr&lt;TagBase&gt;&gt; tags;
};

struct TagA: public TagBase {};
struct TagB: public TagBase {};
struct TagC: public TagBase {};

int main (int argc, const char* argv[])
{
    Namespace nm;
    
    nm.AddTag&lt;TagA&gt;();
    nm.AddTag&lt;TagB&gt;();
    
    assert(nm.HasTag(TagA{}));
    assert(nm.HasTag(TagB{}));
    assert(nm.HasTag(TagC{}) == false);
}

It works, but I have some thoughts about:

  1. Using "typeid" in this context is appropriate? Is this portable?
  2. I can't use raw TagBase class in "std:list" because of slicing. Can I avoid to use "unique_ptr" because of heap allocation?
  3. Maybe there is a better pattern to do this?

I also tried to do this with intense use of templates(std::is_same ... etc) but haven't got proper solution.

答案1

得分: 0

从您的代码中看,std::unordered_set<std::type_index> 似乎可以完成任务:

class Namespace {
public:
    Namespace() = default;

    template<typename T>
    void AddTag() {
        tags.insert(typeid(T));
    }

    template<typename T>
    bool HasTag() const {
        return tags.find(typeid(T)) != tags.end();
    }

private:
    std::unordered_set<std::type_index> tags;
};

struct TagA{};
struct TagB {};
struct TagC {};

int main ()
{
    Namespace nm;
    
    nm.AddTag<TagA>();
    nm.AddTag<TagB>();
    
    assert(nm.HasTag<TagA>());
    assert(nm.HasTag<TagB>());
    assert(nm.HasTag<TagC>() == false);
}

演示

英文:

From your code, std::unordered_set&lt;std::type_index&gt; seems to do the job:

class Namespace {
public:
    Namespace() = default;

    template&lt;typename T&gt;
    void AddTag() {
        tags.insert(typeid(T));
    }

    template&lt;typename T&gt;
    bool HasTag() const {
        return tags.find(typeid(T)) != tags.end();
    }

private:
    std::unordered_set&lt;std::type_index&gt; tags;
};

struct TagA{};
struct TagB {};
struct TagC {};

int main ()
{
    Namespace nm;
    
    nm.AddTag&lt;TagA&gt;();
    nm.AddTag&lt;TagB&gt;();
    
    assert(nm.HasTag&lt;TagA&gt;());
    assert(nm.HasTag&lt;TagB&gt;());
    assert(nm.HasTag&lt;TagC&gt;() == false);
}

Demo

huangapple
  • 本文由 发表于 2023年7月20日 20:09:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76729700.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定