C++ 类枚举计算常数

C++ 类枚举计算常数,c++,c++17,constexpr,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; s

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

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
,例如,如果只是内联将使它变得更容易。

一种简单的非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;
}


问题是,如果您有多个包含IconSet定义的标题(即,它们的编号将根据包含的顺序而变化),那么这将表现得很奇怪,但我认为没有办法避免这种情况。

以下是一种可以使用的方法,基于编译时整数序列上的折叠表达式,按索引实例化图标。结构化绑定可以为您单独命名非静态、非constexpr变量

图标中的匿名命名空间使这些定义仅在此翻译单元中可见,这可能是您想要的,也可能是您不想要的

因此,您可以自己探索代码选项

#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;
}
#包括
#包括
#包括
名称空间图标{
结构IconSet{
constexpr IconSet(大小\u t基准\u偏移量)无例外
:基本偏移量(基本偏移量),图标(基本偏移量*3),图标小(图标+1),图标大(图标+2){
}
尺寸图标;
规模(小);;
尺寸_ticonbig;
尺寸\u t基准\u偏移量\u;
};
模板
constexpr自动生成图标\u助手(std::index\u序列)->std::数组
{
返回{IconSet(Ints)…};
}
模板
constexpr自动生成图标()
{
返回make_icons_helper(std::make_index_sequence{});
}
名称空间{
自动[花、树、森林、山]=制作图标();
}
}
int main()
{
返回图标::forest.iconSmall;
}

这需要一个大注释来指出它依赖于静态初始化顺序。@MaxLanghof我应该将第一句话放在标题字体中吗?:pI的意思不是“你的答案没有提到事实”,而是“这是非常脆弱的代码,应该在接近生产之前非常清楚地标记出来”。)