像计算常量一样枚举

时间:2018-10-31 09:00:44

标签: c++ c++17 constexpr

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

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 个答案:

答案 0 :(得分:9)

这是您可以使用的一种方法,它基于编译时整数序列上的fold表达式,以按索引实例化图标。结构化绑定使您可以分别命名为非静态,非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;
}

答案 1 :(得分:4)

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

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;
}

See it live on Coliru

要注意的是,如果您有几个包含IconSet定义的标头,这将很奇怪(即,其编号将根据包含顺序而变化),但是我认为没有办法避免这种情况