C++ 特性constexpr

本文深入探讨了C++中的const和constexpr关键字的区别,指出const只能确保运行时的不变性,而constexpr则允许在编译时期进行计算。通过示例展示了constexpr在定义常量表达式函数、数组长度和模板函数等方面的应用,并强调了其使用时的限制条件。同时,文章提到了C++14中的if constexpr特性,展示了如何根据类型在编译时选择不同的代码路径。
#include <iostream>
/*
在大多数情况下,const描述的都是一些“运行时常量性”概念
即具有运行时数据的不可更改性。不过有的时候,我们需要的
却是编译时期的常量性,这是const关键字无法保证的;
*/
// 使用const关键字修饰函数返回类型,在数组定义、匿名枚举、switch 的
// case中都会报错,这些语句需要的都是编译时期的常量,而const修饰的函数返回值
// 只保证了运行时期其值不被更改;
const int GetConst() {return 1;}
// void Constless(int cond) {
//     int arr[GetConst()] = {0} // error
//     enum { e1 = GetConst(), e2}; // error
    
//     switch(code) {
//         case GetConst():// error
//             break;
//         default:
//             break;
//     }
// }
// C++11 提供了 constexpr 让用户显式的声明函数或对象构造函数在编译期会成为常量表达式
// 在函数返回类型前加上constexpr关键字(常量表达式-const expression),编译器以在
// 编译时期对fool_constexpr函数进行值计算,从而将其视为编译时期常量
constexpr int fool_constexpr()
{
    return 10;
}

void test1()
{
    // C++array数组长度必须是常量表达式
    int array[10];
    int len1 = 10;
    int array1[len1];    // 非法,len1长度不是常量表达式

    const int len2 = len1 + 10;
    char array2[len2];  // 非法, len2是常数,但不是常量表达式,可能部分编译器成功
    constexpr int len3 = 1 + 2 + 3;
    char array3[len3]; // 合法

    char array4[GetConst() + 2]; // 非法

    // constexpr解决函数运行期间
    char array5[fool_constexpr() + 2]; // 合法,有时遇到这样场景,数组长度来之某个函数返回值,需要用到constexpr

}

// constexpr常量表达式函数运用需要有严格的限制
/*
1.函数体只有单一的return 语句(using,typedef,static_assert等除外);
2.函数必须有返回值(不能是void函数);
3.在使用前必须被定义实现;
4.return 返回的表达式不能使用非常量表达式函数、全局数据,且必须是一个常量表达式;
*/
constexpr int fool() {
    int x = 10;  // 和第1个规则有出入?
    return x;
}

constexpr void voidFool(){}
// constexpr int DeclareFool();// 只有声明没有实现,编译时期需要调用进行计算的


int NoConstExprFunc(){return 1;}
// constexpr int NestedCall() {
//     return NoConstExprFunc(); // 常量表达式中不能使用非常量表达式函数
// }

void test2()
{
    int arr[fool()] = {0};
    for (const auto it: arr) {std::cout<<it<<std::endl;}
    //enum {e1= voidFool(), e2};// error
    //int arr1[DeclareFool()] = {0}; // error


}

// constexpr关键字不能直接用来修饰自定义类型
// constexpr struct Type{int i;}; // error, struct cannot be marked constexpr

// 但可以用来修饰类构造函数,但
// 函数体必须为空和初始化列表只能由常量表达式赋值
struct MyType{
    constexpr MyType(int i_):i(i_){}
    int i;
};

// int ConstExprfool(){
//     return 1;
// }
// struct MyFool{
//     constexpr MyFool():i(ConstExprfool()){} // ConstExprfool 必须是constexpr返回值类型
//     int i;
// };
void test3()
{
    int i = 0;
    //constexpr MyType t = {i}; // error,must be init by a constant expression
    constexpr int ci = 0;
    constexpr MyType t = {ci}; //
}

// constexpr不能用于类的virtual修饰的成员函数,因为
// virtual函数是运行时动态调用的与constexpr编译时计算值是相冲突的

// constexpr用在模板函数中
// NoLiteral构造函数不是constexpr表达式
struct NoLiteral {
    //constexpr NoLiteral(int i_):i(i_){}
    int i;
};

// 当constexpr修饰模板函数时,若模板实例化结果不满足常量
// 表达式, 则constexpr会被自动忽略
template<typename T>
constexpr T GeneratorObj(T t) {
    return t;
}

void test4()
{
    //constexpr NoLiteral n1(1);
    NoLiteral n1(1);
    // n2必须被constexpr表达式初始化
    constexpr NoLiteral n2 = GeneratorObj<NoLiteral>(n1);// error,reutrn non-constexpr NoLiteral object
    
}
// c++14在c++11的constexpr引入了if constexpr控制流,编译时确认分支
template<typename T>
auto printTypeInfo(const T& t){
	if constexpr (std::is_integral<T>::value) {
		return t+1;
	}
	else {
		return t+1.1;
	}
}

void Test5 (){
	printTypeInfo(1);
	/*展开
		printTypeInfo(const T& t){
			return t+1;
		}
	*/
	printTypeInfo(1.1);
	/*
		printTypeInfo(const T& t){
			return t+1.1;
		}
	*/
}
int main()
{
    test2();

    return 0;
}

<think>嗯,用户让我介绍一下C++constexpr,我需要先理清楚这个关键字的用途和演变过程。首先,我得回忆一下constexprC++11中的引入,那时候它主要用于让表达式在编译时求值。早期的constexpr功能比较有限,只能用于简单的函数和常量表达式。 接下来,用户可能想知道constexpr的基本用法。比如,用来声明常量,或者在函数中使用。这时候我需要举一些例子,比如constexpr int a = 5;这样的声明,说明它在编译时确定值,比const更严格。另外,函数方面,C++11中的constexpr函数只能包含一条return语句,可能得举一个计算阶乘的例子,说明递归的使用。 然后,用户可能对C++14和C++17的改进感兴趣。C++14放宽了限制,允许函数内有多个语句,比如局部变量和循环,这时候可以举一个更复杂的例子,比如计算斐波那契数列或者使用循环的例子。而C++17引入了constexpr if,这可能在模板元编程中很有用,可以举一个条件编译的例子,说明如何根据条件选择不同的代码路径。 到了C++20,constexpr的功能进一步增强,支持虚函数、动态内存分配甚至try-catch块。这部分可能需要提到一些高级用例,比如在编译时使用容器,但要注意用户可能对这些新特性不太熟悉,需要解释清楚应用场景和优势。 用户可能还会有疑问,constexpr和const的区别是什么?这时候需要比较两者的不同,强调constexpr的编译时求值特性,以及const更多是运行时的常量性。另外,可能还要提到constexpr在模板元编程中的作用,如何替代传统的模板元编程方法,使得代码更简洁易懂。 我还需要考虑用户的实际应用场景。他们可能在优化性能,或者需要编译时计算来确保某些值的正确性。比如,在游戏开发中,编译时计算矩阵或物理常量,或者在嵌入式系统中减少运行时开销。这时候举一些实际的应用案例会有帮助。 最后,需要总结constexpr的优势,比如提高性能、增强类型安全、使代码更简洁,以及在不同C++标准中的演变。同时,提醒用户在使用时要注意编译器的支持情况,特别是C++20的新特性可能需要较新的编译器版本。 可能用户没有明确提到,但潜在的问题包括如何调试constexpr函数,或者遇到编译错误时的处理方法。这时候可以在回答中简要提到,编译时错误信息可能比较复杂,需要熟悉编译器输出,或者建议逐步测试constexpr函数的功能。 总之,我需要组织一个从基本概念到高级用法,再到实际应用的逐步介绍,确保结构清晰,例子恰当,帮助用户全面理解constexpr的作用和用法。</think>在C++中,`constexpr` 是一个用于编译时计算的关键字,它可以显著提升代码性能和类型安全性。以下是逐步解析: ### 1. **基本概念** - **编译时求值**:`constexpr` 标记的表达式或函数必须**在编译阶段计算出结果**,而非运行时。 - **严格限制**:C++11 初始版本中,`constexpr` 函数只能包含单个 `return` 语句,且不能有副作用。 ```cpp constexpr int square(int x) { return x * x; // C++11 合法 } ``` ### 2. **核心用途** - **常量表达式**:声明编译期常量 ```cpp constexpr int max_size = 100; // 替代 #define 或 const ``` - **编译时计算**:例如数组长度、模板参数 ```cpp int arr[square(5)]; // 数组长度在编译时确定为25 ``` ### 3. **C++14 改进** - **函数限制放宽**: - 允许局部变量、循环和条件语句 ```cpp constexpr int factorial(int n) { int result = 1; for (int i = 1; i <= n; ++i) { result *= i; } return result; // C++14 合法 } ``` ### 4. **C++17 增强** - **`if constexpr`**:编译时条件分支 ```cpp template <typename T> auto get_value(T t) { if constexpr (std::is_pointer_v<T>) { return *t; // 类型为指针时解引用 } else { return t; // 否则直接返回 } } ``` ### 5. **C++20 重大扩展** - **动态内存分配**:支持编译期 `new`/`delete` ```cpp constexpr auto create_array() { int* p = new int[3]{1, 2, 3}; std::array<int, 3> arr{p[0], p[1], p[2]}; delete[] p; return arr; // 编译时构造数组 } ``` - **虚函数**、**try-catch** 等特性支持 ### 6. **与 `const` 的区别** | 特性 | `constexpr` | `const` | |---------------------|--------------------------|------------------------| | 求值时机 | 必须编译时 | 可运行时初始化 | | 适用场景 | 编译期已知的常量 | 运行时常量或只读变量 | | 函数应用 | 可修饰函数 | 不能修饰函数 | ### 7. **典型应用场景** - **数学计算**:编译时计算CRC校验、哈希值 - **元编程**:替代部分模板元编程技巧 - **容器优化**:C++20中编译期构造`std::vector`/`std::string` ### 8. **注意事项** - **编译器支持**:C++20特性需要较新编译器(如GCC 10+) - **调试难度**:编译错误信息可能较为复杂 - **过度使用**:复杂计算可能增加编译时间 ### 总结 `constexpr` 使C++具备了更强的编译时计算能力,逐步从简单的常量定义发展到支持复杂逻辑。合理使用可以: $$ \text{提升性能} + \text{增强类型安全} + \text{简化元编程} $$
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值