C++中存储非类型模板参数:static constexpr与enum数据成员的差异
在C++类实例中存储非类型模板参数:static constexpr vs enum的对比
咱们先把两种常见的实现方案代码贴出来,方便对照着分析:
#include <iostream> // 方案1:static constexpr数据成员 template<int N> struct StaticConstexprDemo { static constexpr int value = N; }; // 方案2:enum声明 template<int N> struct EnumDemo { enum { value = N }; }; int main() { std::cout << StaticConstexprDemo<42>::value << std::endl; std::cout << EnumDemo<42>::value << std::endl; return 0; }
在main函数这种简单场景下,两者输出完全一致,但深究起来,它们的差异和适用场景还是挺多的,咱们来逐个解答你的问题:
1. 二者是否存在互不可替代的操作?
当然有,两者的特性差异导致了一些场景下只能用其中一种:
只能用static constexpr的场景
- 需要取地址/传递引用:枚举成员是枚举类型的编译期符号,没法获取它的地址(
&EnumDemo<42>::value是非法的),而static constexpr成员是真正的静态变量,可以取地址,能作为需要指针或引用参数的函数的实参。比如你想把这个值的指针传给某个函数,只能用static constexpr方案。 - 存储浮点型非类型模板参数:C++标准规定枚举的底层类型必须是整数类型,所以如果你的非类型模板参数是
double或者float,enum方案直接没法用,只能靠static constexpr来存储。 - 需要类外补充定义(旧标准下):虽然C17之后static constexpr默认是inline的,但在C11/14中,如果需要ODR使用(比如取地址),可以在类外补充定义,而enum成员完全没有这个操作空间,它的值在类内就固定死了。
只能用enum的场景
- 彻底避免ODR问题:如果只是使用枚举成员的值,它不会触发One Definition Rule(ODR),不需要任何额外的定义;而static constexpr成员在C++17之前,如果被ODR使用(比如取地址),必须在类外写定义(比如
template<int N> constexpr int StaticConstexprDemo<N>::value;),否则链接器可能报错。 - C++03及更早版本的编译期常量存储:在constexpr出现之前(C++11之前),static const虽然能存整数常量,但有很多限制,而enum是最可靠的编译期常量存储方式,这时候static constexpr根本不存在,只能用enum。
- 不占用任何内存空间:枚举成员是编译期的符号,不会在程序的内存空间中分配实体;而static constexpr成员即使是静态的,虽然不占用类实例的内存,但如果被ODR使用,会在静态存储区分配内存。如果你追求极致的零内存开销,enum是更好的选择。
2. 哪些C++版本允许或禁用这两种方案?
enum方案
- 从C++98开始就完全支持,只要非类型模板参数是整数类型(包括bool、char、short、int、long等),都可以用enum来存储它的值。C++11引入的强类型枚举(enum class)也可以做同样的事,但普通enum的兼容性更好。
- 注意:enum始终只能存储整数类型的编译期常量,浮点型非类型模板参数在任何C++版本中都不能用enum存储。
static constexpr方案
- 从C++11开始引入,因为constexpr关键字是C++11才有的。
- C++11/14中:static constexpr成员可以在类内初始化,但如果被ODR使用(比如取地址),必须在类外补充定义;
- C++17及以后:static constexpr默认是inline的,即使被ODR使用,也不需要类外定义,使用起来更方便。
- C++03及更早版本没有constexpr,所以没法用这个方案,只能用enum或者static const(但static const在类内初始化只支持整数类型,且同样有ODR问题)。
内容的提问来源于stack exchange,提问作者T.L
相关产品推荐
相关产品推荐

