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 expression
8 | constexpr int y2 = val;
| ^~~
<source>:5:9: note: 'int val' is not const
5 | int val;
| ^~~
在这个代码段中,val的值是根据输入而变化的,运行期才能确定val,显然与constexpr的编译期特性不符合
此外, 常量表达式指针: constexpr 位于 * 左侧,但表示指针是常量表达式 ,constexpr是作为“修饰”,而不是作为一种变量类型:
#include <iostream>
#include <type_traits>
int main()
{
constexpr const int* ptr = nullptr; // ptr --> const int* const
std::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 rbp
mov rbp, rsp
sub rsp, 16
mov QWORD PTR [rbp-8], 832040
mov rax, QWORD PTR [rbp-8]
mov rsi, rax
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(long)
mov eax, 0
leave
ret
__static_initialization_and_destruction_0(int, int):
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], edi
mov DWORD PTR [rbp-8], esi
cmp DWORD PTR [rbp-4], 1
jne .L5
cmp DWORD PTR [rbp-8], 65535
jne .L5
mov edi, OFFSET FLAT:_ZStL8__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:_ZStL8__ioinit
mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
call __cxa_atexit
.L5:
nop
leave
ret
_GLOBAL__sub_I_main:
push rbp
mov rbp, rsp
mov esi, 65535
mov edi, 1
call __static_initialization_and_destruction_0(int, int)
pop rbp
ret
#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 rbp
mov rbp, rsp
push rbx
sub rsp, 24
mov DWORD PTR [rbp-20], edi
cmp DWORD PTR [rbp-20], 1
jg .L2
mov eax, DWORD PTR [rbp-20]
cdqe
jmp .L4
.L2:
mov eax, DWORD PTR [rbp-20]
sub eax, 1
mov edi, eax
call fib(int)
mov rbx, rax
mov eax, DWORD PTR [rbp-20]
sub eax, 2
mov edi, eax
call fib(int)
add rax, rbx
.L4:
mov rbx, QWORD PTR [rbp-8]
leave
ret
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov edi, 30
call fib(int)
mov QWORD PTR [rbp-8], rax
mov rax, QWORD PTR [rbp-8]
mov rsi, rax
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(long)
mov eax, 0
leave
ret
__static_initialization_and_destruction_0(int, int):
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], edi
mov DWORD PTR [rbp-8], esi
cmp DWORD PTR [rbp-4], 1
jne .L9
cmp DWORD PTR [rbp-8], 65535
jne .L9
mov edi, OFFSET FLAT:_ZStL8__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:_ZStL8__ioinit
mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
call __cxa_atexit
.L9:
nop
leave
ret
_GLOBAL__sub_I_fib(int):
push rbp
mov rbp, rsp
mov esi, 65535
mov edi, 1
call __static_initialization_and_destruction_0(int, int)
pop rbp
ret
两段代码唯一的不同就是const long int fit(n) --> constexpr long int fit(n)
从汇编代码看出,第一段代码的汇编代码没有出现fit函数,在编译期编译器就计算fit函数并返回结果,也说明constexpr对应编译期常量。
两者有不同的用途。 constexpr 主要用于优化,而 const 主要用于实际的常量对象,如 Pi 值。它们都可以应用于成员方法。成员方法是常量,以确保没有意外的变化,由该方法。另一方面,使用 constexpr 的想法是在编译时计算表达式,以便在运行代码时节省时间。const 只能用于非静态成员函数,而 constexpr 可以用于成员函数和非成员函数,甚至可以用于构造函数,但条件是参数和返回类型必须是文本类型。