constexpr( > C++11)
constexpr = const expression,意思就是常量表达式。声明的是编译器常量,其主要思想是通过在编译时而不是运行时进行计算来提高程序的性能,,起到优化程序的作用。constexpr 指定对象或函数的值可以在编译时计算,表达式可以在其他常量表达式中使用。
#include <iostream>constexpr int product(int x, int y){return (x * y);}int main(){const int x = product(10, 20);cout << x;return 0;}
Output:
200
constexpr函数注意事项:
- 在 c + + 11中,constexpr 函数应该只包含一个返回语句,c + + 14允许多个语句。
- constexpr 函数应该只引用常量全局变量。
- constexpr函数只能调用其他恒定扩展函数,不能调用简单函数。
- constexp函数不应该是 void 类型的,并且在 constexpr 函数中不允许使用前缀增量(+ + v)等操作符。
注意,constexpr声明的是编译期常量,这点与const不同:
#include <iostream>int main(){int val;std::cin >> val;const int y1 =val;constexpr int y2 = val;}
Output:
<source>: In function 'int main()':<source>:8:24: error: the value of 'val' is not usable in a constant expression8 | constexpr int y2 = val;| ^~~<source>:5:9: note: 'int val' is not const5 | int val;| ^~~
在这个代码段中,val的值是根据输入而变化的,运行期才能确定val,显然与constexpr的编译期特性不符合
此外, 常量表达式指针: constexpr 位于 * 左侧,但表示指针是常量表达式 ,constexpr是作为“修饰”,而不是作为一种变量类型:
#include <iostream>#include <type_traits>int main(){constexpr const int* ptr = nullptr; // ptr --> const int* conststd::cout << std::is_same_v <decltype(ptr),const int* const> << std::endl;}
Output:
1
const
constexpr VS const
#include<iostream>using namespace std;constexpr long int fib(int n){return (n <= 1)? n : fib(n-1) + fib(n-2);}int main (){// value of res is computed at compile time.long int res = fib(30);cout << res;return 0;}
汇编代码:
main:push rbpmov rbp, rspsub rsp, 16mov QWORD PTR [rbp-8], 832040mov rax, QWORD PTR [rbp-8]mov rsi, raxmov edi, OFFSET FLAT:_ZSt4coutcall std::basic_ostream<char, std::char_traits<char> >::operator<<(long)mov eax, 0leaveret__static_initialization_and_destruction_0(int, int):push rbpmov rbp, rspsub rsp, 16mov DWORD PTR [rbp-4], edimov DWORD PTR [rbp-8], esicmp DWORD PTR [rbp-4], 1jne .L5cmp DWORD PTR [rbp-8], 65535jne .L5mov edi, OFFSET FLAT:_ZStL8__ioinitcall std::ios_base::Init::Init() [complete object constructor]mov edx, OFFSET FLAT:__dso_handlemov esi, OFFSET FLAT:_ZStL8__ioinitmov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Evcall __cxa_atexit.L5:nopleaveret_GLOBAL__sub_I_main:push rbpmov rbp, rspmov esi, 65535mov edi, 1call __static_initialization_and_destruction_0(int, int)pop rbpret
#include<iostream>using namespace std;const long int fib(int n){return (n <= 1)? n : fib(n-1) + fib(n-2);}int main (){// value of res is computed at compile time.long int res = fib(30);cout << res;return 0;}
汇编代码:
fib(int):push rbpmov rbp, rsppush rbxsub rsp, 24mov DWORD PTR [rbp-20], edicmp DWORD PTR [rbp-20], 1jg .L2mov eax, DWORD PTR [rbp-20]cdqejmp .L4.L2:mov eax, DWORD PTR [rbp-20]sub eax, 1mov edi, eaxcall fib(int)mov rbx, raxmov eax, DWORD PTR [rbp-20]sub eax, 2mov edi, eaxcall fib(int)add rax, rbx.L4:mov rbx, QWORD PTR [rbp-8]leaveretmain:push rbpmov rbp, rspsub rsp, 16mov edi, 30call fib(int)mov QWORD PTR [rbp-8], raxmov rax, QWORD PTR [rbp-8]mov rsi, raxmov edi, OFFSET FLAT:_ZSt4coutcall std::basic_ostream<char, std::char_traits<char> >::operator<<(long)mov eax, 0leaveret__static_initialization_and_destruction_0(int, int):push rbpmov rbp, rspsub rsp, 16mov DWORD PTR [rbp-4], edimov DWORD PTR [rbp-8], esicmp DWORD PTR [rbp-4], 1jne .L9cmp DWORD PTR [rbp-8], 65535jne .L9mov edi, OFFSET FLAT:_ZStL8__ioinitcall std::ios_base::Init::Init() [complete object constructor]mov edx, OFFSET FLAT:__dso_handlemov esi, OFFSET FLAT:_ZStL8__ioinitmov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Evcall __cxa_atexit.L9:nopleaveret_GLOBAL__sub_I_fib(int):push rbpmov rbp, rspmov esi, 65535mov edi, 1call __static_initialization_and_destruction_0(int, int)pop rbpret
两段代码唯一的不同就是const long int fit(n) --> constexpr long int fit(n)
从汇编代码看出,第一段代码的汇编代码没有出现fit函数,在编译期编译器就计算fit函数并返回结果,也说明constexpr对应编译期常量。
两者有不同的用途。 constexpr 主要用于优化,而 const 主要用于实际的常量对象,如 Pi 值。它们都可以应用于成员方法。成员方法是常量,以确保没有意外的变化,由该方法。另一方面,使用 constexpr 的想法是在编译时计算表达式,以便在运行代码时节省时间。const 只能用于非静态成员函数,而 constexpr 可以用于成员函数和非成员函数,甚至可以用于构造函数,但条件是参数和返回类型必须是文本类型。
