定义编译时常量的最佳方法

时间:2015-02-27 00:25:16

标签: c++ c++11 constants constexpr idiomatic

在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尽可能接近字面值,所以也许这是避免“过度思考”的最佳选择。问题?我想普遍的共识是,你只希望没有人需要使用Opaque这个词或你想要使用的其他常量吗?

3 个答案:

答案 0 :(得分:12)

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

只是明确重申要求:

  1. 应该没有运行时计算。
  2. 除实际结果外,不应有静态,堆栈或堆内存分配。 (可执行代码的分配是不可能禁止的,但要确保CPU所需的任何数据存储都是私有的。)
  3. 在C ++中,表达式可以是左值或右值(在C ++ 11和C之前, rvalues 对应于相关概念)。 Lvalues引用对象,因此它们可以出现在赋值表达式的 L eft-hand侧。对象存储和左值是我们想要避免的。

    您想要的是一个标识符,或 id-expression ,以评估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()。函数调用操作符可能看起来效率较低,但它是完全消除存储开销的唯一当前方法。

答案 1 :(得分:1)

你可能会出错:

static constexpr const unsigned int Value = 123;

老实说,尽量不要关心这个。就像,真的试试。

答案 2 :(得分:1)

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

enum : unsigned int { Value = 123 };

这消除了您对使用枚举的两个异议之一,即您无法控制实际用于表示它们的类型。但它仍然不允许非整数常量。