像计算出的常量一样的枚举

10

实际上这个“问题”非常简单。在计算图标偏移量时,我想到了以下方法:

namespace Icons {

  struct IconSet {
    constexpr IconSet(size_t base_offset) noexcept
      : base_offset_(base_offset), icon(base_offset * 3), iconSmall(icon + 1), iconBig(icon + 2) {
    }
    size_t icon;
    size_t iconSmall;
    size_t iconBig;

    size_t base_offset_;

    constexpr size_t next() const {
      return base_offset_ + 1;
    }
  };


  static constexpr IconSet flower = IconSet(0);
  static constexpr IconSet tree = IconSet(flower.next());
  static constexpr IconSet forest = IconSet(tree.next());
  static constexpr IconSet mountain = IconSet(forest.next());

 }

现在,例如可以编写Icons::tree.iconBig来获取该图标的计算偏移量。基本上,设计师可以更改图标 - 有时也会添加/删除 - 但是按照惯例始终必须提供整个集(普通、小和大)。

正如您所看到的,这种方法的问题在于我不得不执行next()函数并重复使用它 - 正常的枚举类型不会有这种缺点。

我知道BOOST_PP和其他宏技巧,但我希望有一些不需要宏的东西 - 因为我有一种感觉不需要它,我比较喜欢我已经用简单的next()函数实现的功能。 当然,另一个解决方案只是普通的枚举类型和一个计算函数,但那就失去了预计算的目的。

因此,我正在寻找一个简单且可移植的解决方案,可以提供类似枚举类型的功能。如果例如只内联,它不必是编译时或constexpr

2个回答

9
这里有一种方法,可以使用编译时整数序列上的折叠表达式来按索引实例化图标。结构化绑定使您获得了命名良好的非静态、非constexpr变量。 Icons内的匿名命名空间仅在此翻译单位中使这些定义可见,这可能是您需要的或不需要的。 Compiler explorer link,以便您可以自行探索代码选项。
#include <cstddef>
#include <array>
#include <utility>

namespace Icons {

struct IconSet {
    constexpr IconSet(size_t base_offset) noexcept 
      : base_offset_(base_offset), icon(base_offset * 3), iconSmall(icon + 1), iconBig(icon + 2) {
    }
    size_t icon;
    size_t iconSmall;
    size_t iconBig;

    size_t base_offset_;
};

template <std::size_t... Ints>
constexpr auto make_icons_helper(std::index_sequence<Ints...>) -> std::array<IconSet, sizeof...(Ints)> 
{
    return {IconSet(Ints)...};
}

template <size_t N>
constexpr auto make_icons()
{
    return make_icons_helper(std::make_index_sequence<N>{});
}

namespace {
    auto [flower, tree, forest, mountain] = make_icons<4>();
}

}

int main()
{
    return Icons::forest.iconSmall;
}

4

一个简单的非constexpr解决方案,使用静态计数器,并依赖于静态初始化在单个TU内自上而下执行的事实:

namespace Icons {
  namespace detail_iconSet {
    static std::size_t current_base_offset = 0;
  }

  struct IconSet {
    IconSet() noexcept 
      : base_offset_(detail_iconSet::current_base_offset++)
      , icon(base_offset_ * 3)
      , iconSmall(icon + 1)
      , iconBig(icon + 2) { }

    std::size_t base_offset_;

    std::size_t icon;
    std::size_t iconSmall;
    std::size_t iconBig;
  };


  static IconSet flower;
  static IconSet tree;
  static IconSet forest;
  static IconSet mountain;
}

在 Coliru 上实时查看

问题是,如果您有多个包含 IconSet 定义的标题,则它将表现奇怪(即其编号将根据包含顺序而更改),但我认为没有避免这种情况的方法。


这需要一个大而明显的注释来指出它依赖于静态初始化顺序。 - Max Langhof
1
@MaxLanghof 我应该把第一句话放在标题字体中吗? :p - Quentin
1
我并不是指“你的回答没有提到这一事实”,而是指“这段代码非常脆弱,在进入生产之前应该非常明确地标注为此”。 ;) - Max Langhof

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接