强制 class 的 class const 成员在编译时求值

Force in class const member of class to be evaluated at compile time

我正在使用 SFML 创建 Snake 游戏,我有一个具体的 class SnakeGame 包含我 class 的所有数据成员,例如 window 大小,游戏地图的大小,蛇的颜色等。因此,我的 class 的内部成员如下所示:

class SnakeGame
{
private:
    //....
    const sf::Vector2u windowSize{400, 336};
    const sf::Color snakeColor {0, 0, 0};
    //...etc...
public:
    SnakeGame() : 
        renderWindow {sf::VideoMode{windowSize.x, windowSize.y}, /*other arguments*/ },
        snake {snakeColor, /*other arguments*/} 
        /*etc*/ 
    {}

但是,这不起作用,我得到一个不可见的 window,这使我相信参数尚未构建。在阅读了另一个 SO post 之后,我读到 const 成员作为 class 的成员不会在编译时求值,而是在实例化 class 时在运行时求值,并且它们的行为方式与 const 变量完全相同。因此,当我在 main 中实例化 SnakeGame 时,会评估变量 windowSizesnakeColor,因此我不能在我的 class 的初始化列表中使用它们。为了尝试解决这个困境,我决定切换到 static constexpr 变量,但不幸的是我意识到 sf::Vector<T> 没有 constexpr 构造函数,sf::Color 也没有。当然,我想我可以只切换到整数类型,例如 window 大小的 static constexpr unsigned int,但最终这会变得重复,尤其是对于颜色。所以我决定走另一条路。我所做的是创建另一个文件 GameData.hpp,并在其中执行此操作:

namespace gd //For game data
{
    const sf::Vector2u windowSize{400, 336};
    const sf::Color snakeColor {0, 0, 0};
    //...etc...
}

但是,我不喜欢这个解决方案,因为即使现在在编译时评估这个数据,如果它们包含适当的头文件,我的所有 classes 也可以访问它,而不仅仅是为了SnakeGame。这样一来,感觉我的class的内部数据结构就暴露了。这引出了我的问题,即:有没有一种方法可以强制 class 的 const 成员在编译时进行评估,以便这些变量可以用于保证它们将被构造的初始化列表?

这里有几个选项,所有选项都需要权衡取舍。我按照我认为最好到最差的粗略顺序对它们进行了排名,但您的权衡可能会有所不同。

  • 重构您的代码,使颜色和 window 尺寸不再是全局常量。作为一名玩家,我想要一条绿蛇,而不是一条黑蛇,我想要一个两倍大的棋盘。这可能涉及将它们保留为成员变量,但这次不是常量,也许 re-ordering 它们这样您就可以在其他成员变量的初始化中使用它们。
  • 将编译器设置为 C++17 模式,并使 snakeColor 成为 inline static const 变量。如果你想要一个 header-only 库并且可以使用 C++17,这是最好的选择。
  • 使其成为const static数据成员,并将const sf::Color SnakeGame::snakeColor {0, 0, 0}添加到恰好一个.cpp文件中。如果您不想要 header-only 库,这是最好的选择。
  • 创建一个 getColor static const sf::Color& getSnakeColor() 方法,其中有一个 static 常量,return。 static 关键字在函数内部的含义略有不同,您可以在那里做您想做的事。但是,这意味着您需要更改使用它的语法。
  • 按照您在回答中的建议使用命名空间。如果其他所有内容都在 namespace snake 中,那么您将这些变量放在 snake::details 中,那么按照惯例人们会知道他们不应该看那里。与其他选项不同,这不是强制执行的。
  • 使用 SFML 提交功能请求以将 Vector2uColor 转换为文字类型,以便您可以使用 constexpr。显然这需要更长的时间并且不能保证有效。
  • Re-order class 以更有用的顺序排列成员,as suggested by VTT in the comments。这与第一个选项相同,我说过我最喜欢它,但是将它们保持为常量,对于 class 的每个实例都具有相同的值。我把它放在列表底部的原因是,这意味着 你的 class 的每个副本 都会携带一份颜色和 window 大小,当你实际上只需要一个时。

可能还有其他选项我忘记了。例如,有一种涉及辅助模板 class 的方法,您可以使用它来获取 header-only 库,而无需 inline 成员变量支持,也无需切换到函数调用来访问数据,以及强制执行 private 说明符的位置。不过,我不太记得如何让它发挥作用,而且它比通常值得的要复杂得多。