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

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

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

问题

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

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

  1. #include <memory>
  2. #include <list>
  3. struct TagBase {
  4. bool operator== (const TagBase& other) const {
  5. return typeid(*this) == typeid(other);
  6. }
  7. bool operator!= (const TagBase& other) const {
  8. return !(*this == other);
  9. }
  10. virtual ~TagBase() = default;
  11. };
  12. bool operator== (const std::unique_ptr<TagBase>& lhs, const TagBase& base) {
  13. return base == *(lhs.get());
  14. }
  15. class Namespace {
  16. public:
  17. template<typename T>
  18. void AddTag() {
  19. tags.push_back(std::unique_ptr<T>(new T()));
  20. }
  21. bool HasTag(const TagBase& tag) const {
  22. return std::find_if(tags.begin(), tags.end(), [&tag](const std::unique_ptr<TagBase>& ptr) {
  23. return *ptr == tag;
  24. }) != tags.end();
  25. }
  26. private:
  27. // 使用 std::unique_ptr 是唯一的方式吗?
  28. std::list<std::unique_ptr<TagBase>> tags;
  29. };
  30. struct TagA: public TagBase {};
  31. struct TagB: public TagBase {};
  32. struct TagC: public TagBase {};
  33. int main (int argc, const char* argv[])
  34. {
  35. Namespace nm;
  36. nm.AddTag<TagA>();
  37. nm.AddTag<TagB>();
  38. assert(nm.HasTag(TagA{}));
  39. assert(nm.HasTag(TagB{}));
  40. assert(nm.HasTag(TagC{}) == false);
  41. }

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

  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:

  1. #include &lt;memory&gt;
  2. #include &lt;list&gt;
  3. struct TagBase {
  4. bool operator== (const TagBase&amp; other) const {
  5. return typeid(*this) == typeid(other);
  6. }
  7. bool operator!= (const TagBase&amp; other) const {
  8. return !(*this == other);
  9. }
  10. virtual ~TagBase() = default;
  11. };
  12. bool operator== (const std::unique_ptr&lt;TagBase&gt;&amp; lhs, const TagBase&amp; base) {
  13. return base == *(lhs.get());
  14. }
  15. class Namespace {
  16. public:
  17. template&lt;typename T&gt;
  18. void AddTag() {
  19. tags.push_back(std::unique_ptr&lt;T&gt;(new T()));
  20. }
  21. bool HasTag(const TagBase&amp; tag) const {
  22. return std::find(std::begin(tags), std::end(tags), tag) != tags.end();
  23. }
  24. private:
  25. // using of std::unique_ptr is the only way?
  26. std::list&lt;std::unique_ptr&lt;TagBase&gt;&gt; tags;
  27. };
  28. struct TagA: public TagBase {};
  29. struct TagB: public TagBase {};
  30. struct TagC: public TagBase {};
  31. int main (int argc, const char* argv[])
  32. {
  33. Namespace nm;
  34. nm.AddTag&lt;TagA&gt;();
  35. nm.AddTag&lt;TagB&gt;();
  36. assert(nm.HasTag(TagA{}));
  37. assert(nm.HasTag(TagB{}));
  38. assert(nm.HasTag(TagC{}) == false);
  39. }

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> 似乎可以完成任务:

  1. class Namespace {
  2. public:
  3. Namespace() = default;
  4. template<typename T>
  5. void AddTag() {
  6. tags.insert(typeid(T));
  7. }
  8. template<typename T>
  9. bool HasTag() const {
  10. return tags.find(typeid(T)) != tags.end();
  11. }
  12. private:
  13. std::unordered_set<std::type_index> tags;
  14. };
  15. struct TagA{};
  16. struct TagB {};
  17. struct TagC {};
  18. int main ()
  19. {
  20. Namespace nm;
  21. nm.AddTag<TagA>();
  22. nm.AddTag<TagB>();
  23. assert(nm.HasTag<TagA>());
  24. assert(nm.HasTag<TagB>());
  25. assert(nm.HasTag<TagC>() == false);
  26. }

演示

英文:

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

  1. class Namespace {
  2. public:
  3. Namespace() = default;
  4. template&lt;typename T&gt;
  5. void AddTag() {
  6. tags.insert(typeid(T));
  7. }
  8. template&lt;typename T&gt;
  9. bool HasTag() const {
  10. return tags.find(typeid(T)) != tags.end();
  11. }
  12. private:
  13. std::unordered_set&lt;std::type_index&gt; tags;
  14. };
  15. struct TagA{};
  16. struct TagB {};
  17. struct TagC {};
  18. int main ()
  19. {
  20. Namespace nm;
  21. nm.AddTag&lt;TagA&gt;();
  22. nm.AddTag&lt;TagB&gt;();
  23. assert(nm.HasTag&lt;TagA&gt;());
  24. assert(nm.HasTag&lt;TagB&gt;());
  25. assert(nm.HasTag&lt;TagC&gt;() == false);
  26. }

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:

确定