定义编译时常量的最佳方式
Best way of defining a compile-time constant
在 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
的值在运行时不会改变,因为它只在编译时需要,所以让它出现在我的编译代码中似乎很愚蠢。这些值也在一个循环中使用,该循环为图像中的每个像素运行多次,所以我想避免运行时查找会减慢它的速度(例如,内存访问以在运行时检索常量的值。)这是' t 过早优化,因为该算法目前处理一张图像大约需要一秒钟,而我通常要处理 100 多张图像,所以我希望它尽可能快。
既然人们说这是微不足道的,不用担心,我猜 #define
是尽可能接近字面值,所以也许这是避免 "overthinking" 问题?我想普遍的共识是你只是希望没有人需要使用这个词 Opaque
或你想使用的其他常量?
你不会出错的:
static constexpr const unsigned int Value = 123;
老实说,尽量不要关心这个。喜欢,真的试试。
事实上,这比看起来要棘手。
只是为了明确重申要求:
- 应该没有运行时计算。
- 除实际结果外,不应有静态、堆栈或堆内存分配。 (分配可执行代码是不可能禁止的,但要确保 CPU 所需的任何数据存储都是私有的。)
在C++中,表达式可以是左值或纯右值(在C++11之前和C中,右值对应相关概念)。左值引用对象,因此它们可以出现在赋值表达式的 L 左手边。对象存储和左值是我们要避免的。
您需要的是一个标识符,或 id-expression,以计算纯右值。
目前,只有普查员可以做到这一点,但正如您所观察到的,他们还有一些不足之处。每个枚举声明都引入了一个新的、不同的类型,因此 enum { Value = 123 };
引入了一个常量,它不是整数,而是它自己的唯一类型, 将 转换为 int
。这不是完成这项工作的正确工具,尽管它在紧要关头工作。
您可以使用 #define
,但这是一种 hack,因为它完全避开了解析器。您必须用全部大写字母命名它,然后确保相同的全大写名称不会用于程序中的任何其他内容。对于库接口,这样的保证尤其繁重。
下一个最佳选择是函数调用:
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()
。函数调用运算符可能看起来效率较低,但它是当前唯一完全消除存储开销的方法。
我认为您应该考虑 C++11 feature of specifying an underlying type for an enum,它适用于您的示例:
enum : unsigned int { Value = 123 };
这消除了您对使用枚举的两个反对意见之一,即您无法控制实际使用哪种类型来表示它们。不过,它仍然不允许非整数常量。
在 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
的值在运行时不会改变,因为它只在编译时需要,所以让它出现在我的编译代码中似乎很愚蠢。这些值也在一个循环中使用,该循环为图像中的每个像素运行多次,所以我想避免运行时查找会减慢它的速度(例如,内存访问以在运行时检索常量的值。)这是' t 过早优化,因为该算法目前处理一张图像大约需要一秒钟,而我通常要处理 100 多张图像,所以我希望它尽可能快。
既然人们说这是微不足道的,不用担心,我猜 #define
是尽可能接近字面值,所以也许这是避免 "overthinking" 问题?我想普遍的共识是你只是希望没有人需要使用这个词 Opaque
或你想使用的其他常量?
你不会出错的:
static constexpr const unsigned int Value = 123;
老实说,尽量不要关心这个。喜欢,真的试试。
事实上,这比看起来要棘手。
只是为了明确重申要求:
- 应该没有运行时计算。
- 除实际结果外,不应有静态、堆栈或堆内存分配。 (分配可执行代码是不可能禁止的,但要确保 CPU 所需的任何数据存储都是私有的。)
在C++中,表达式可以是左值或纯右值(在C++11之前和C中,右值对应相关概念)。左值引用对象,因此它们可以出现在赋值表达式的 L 左手边。对象存储和左值是我们要避免的。
您需要的是一个标识符,或 id-expression,以计算纯右值。
目前,只有普查员可以做到这一点,但正如您所观察到的,他们还有一些不足之处。每个枚举声明都引入了一个新的、不同的类型,因此 enum { Value = 123 };
引入了一个常量,它不是整数,而是它自己的唯一类型, 将 转换为 int
。这不是完成这项工作的正确工具,尽管它在紧要关头工作。
您可以使用 #define
,但这是一种 hack,因为它完全避开了解析器。您必须用全部大写字母命名它,然后确保相同的全大写名称不会用于程序中的任何其他内容。对于库接口,这样的保证尤其繁重。
下一个最佳选择是函数调用:
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()
。函数调用运算符可能看起来效率较低,但它是当前唯一完全消除存储开销的方法。
我认为您应该考虑 C++11 feature of specifying an underlying type for an enum,它适用于您的示例:
enum : unsigned int { Value = 123 };
这消除了您对使用枚举的两个反对意见之一,即您无法控制实际使用哪种类型来表示它们。不过,它仍然不允许非整数常量。