代码之家  ›  专栏  ›  技术社区  ›  Malvineous

定义编译时间常数的最佳方法

  •  16
  • Malvineous  · 技术社区  · 10 年前

    在C++11中定义一个简单常量值的最佳方法是什么?例如:(无效代码)

    // Not ideal, no type, easy to put in wrong spot and get weird errors
    #define VALUE 123
    
    // Ok, but integers only, and is it int, long, uint64_t or what?
    enum {
         Value = 123
    };
    
    // Could be perfect, but does the variable take up memory at runtime?
    constexpr unsigned int Value = 123;
    
    class MyClass {
        // What about a constant that is only used within a class, so
        // as not to pollute the parent namespace?  Does this take up
        // memory every time the class is instantiated?  Does 'static'
        // change anything?
        constexpr unsigned int Value = 123;
    
        // What about a non-integer constant?
        constexpr const char* purpose = "example";
        std::string x;
        std::string metadata() { return this->x + (";purpose=" purpose); }
        // Here, the compiled code should only have one string
        // ";purpose=example" in it, and "example" on its own should not
        // be needed.
    };
    

    编辑

    既然有人告诉我这是一个无用的问题,因为它背后没有背景,下面是背景。

    我正在定义一些标志,这样我就可以这样做:

    if (value & Opaque) { /* do something */ }
    

    的值 Opaque 不会在运行时更改,因为它只在编译时需要,所以让它在我的编译代码中结束似乎很愚蠢。这些值也在一个循环中使用,该循环为图像中的每个像素运行多次,因此我希望避免运行时查找会使其变慢(例如,在运行时访问内存以检索常量值)。这不是过早的优化,因为算法当前处理一个图像需要大约一秒钟,而我通常要处理100多个图像,所以我希望它尽可能快。

    既然人们都说这是小事,不用担心,我猜 #define 尽可能接近字面值,所以这也许是避免“过度思考”问题的最佳选择?我想普遍的共识是你只是希望没有人需要使用这个词 不透明的 或者你想使用的其他常数?

    3 回复  |  直到 9 年前
        1
  •  13
  •   Potatoswatter    10 年前

    事实上,这比看起来更棘手。

    只是为了明确重申要求:

    1. 不应有运行时计算。
    2. 除了实际结果之外,不应该有静态、堆栈或堆内存分配。(不可能禁止分配可执行代码,但要确保CPU所需的任何数据存储都是私有的。)

    在C++中,表达式可以是lvalues或prvalues(在C++11之前, 右值 对应于相关概念)。L值指的是对象,因此它们可以出现在 L 赋值表达式的左手边。对象存储和左值是我们想要避免的。

    您需要的是标识符,或者 id表达式 ,计算为prvalue。

    目前,只有枚举器可以做到这一点,但正如您所观察到的,它们会留下一些不需要的东西。每个枚举声明都引入了一个新的、不同的类型,因此 enum { Value = 123 }; 引入一个常量,该常量不是整数,而是它自己的唯一类型 转换 int 。这不是适合这份工作的工具,尽管它在紧要关头也能奏效。

    您可以使用 #define ,但这是一个黑客,因为它完全避免了解析器。你必须用所有大写字母来命名它,然后确保程序中的任何其他内容都不使用相同的全大写名称。对于库接口,这样的保证尤其繁重。

    下一个最佳选项是函数调用:

    constexpr int value() { return 123; }
    

    不过要小心,因为 constexpr 函数仍然可以在运行时求值。您需要再跳一圈,将该值表示为计算值:

    constexpr int value() {
        /* Computations that do not initialize constexpr variables
           (or otherwise appear in a constant expression context)
           are not guaranteed to happen at compile time, even
           inside a constexpr function. */
    
        /* It's OK to initialize a local variable because the
           storage is only temporary. There's no more overhead
           here than simply writing the number or using #define. */
    
        constexpr int ret = 120 + 3;
        return ret;
    }
    

    现在,不能将常量称为名称,它必须是 value() 。函数调用运算符可能看起来效率较低,但这是目前唯一完全消除存储开销的方法。

        2
  •  3
  •   einpoklum    8 年前

    我认为你应该考虑 C++11 feature of specifying an underlying type for an enum ,适用于您的示例的是:

    enum : unsigned int { Value = 123 };
    

    这消除了您对使用enum的两个反对意见之一,即无法控制实际使用哪种类型来表示它们。但它仍然不允许非整数常量。

        3
  •  0
  •   Lightness Races in Orbit    10 年前

    你不会错的:

    static constexpr const unsigned int Value = 123;
    

    但老实说,尽量不要在意这一点。比如,真的试试看。