constexpr( > C++11)
    constexpr = const expression,意思就是常量表达式。声明的是编译器常量,其主要思想是通过在编译时而不是运行时进行计算来提高程序的性能,,起到优化程序的作用。constexpr 指定对象或函数的值可以在编译时计算,表达式可以在其他常量表达式中使用。

    1. #include <iostream>
    2. constexpr int product(int x, int y)
    3. {
    4. return (x * y);
    5. }
    6. int main()
    7. {
    8. const int x = product(10, 20);
    9. cout << x;
    10. return 0;
    11. }

    Output:

    1. 200

    constexpr函数注意事项:

    1. 在 c + + 11中,constexpr 函数应该只包含一个返回语句,c + + 14允许多个语句。
    2. constexpr 函数应该只引用常量全局变量。
    3. constexpr函数只能调用其他恒定扩展函数,不能调用简单函数。
    4. constexp函数不应该是 void 类型的,并且在 constexpr 函数中不允许使用前缀增量(+ + v)等操作符。

    注意,constexpr声明的是编译期常量,这点与const不同:

    1. #include <iostream>
    2. int main()
    3. {
    4. int val;
    5. std::cin >> val;
    6. const int y1 =val;
    7. constexpr int y2 = val;
    8. }

    Output:

    1. <source>: In function 'int main()':
    2. <source>:8:24: error: the value of 'val' is not usable in a constant expression
    3. 8 | constexpr int y2 = val;
    4. | ^~~
    5. <source>:5:9: note: 'int val' is not const
    6. 5 | int val;
    7. | ^~~

    在这个代码段中,val的值是根据输入而变化的,运行期才能确定val,显然与constexpr的编译期特性不符合

    此外, 常量表达式指针: constexpr 位于 * 左侧,但表示指针是常量表达式 ,constexpr是作为“修饰”,而不是作为一种变量类型:

    1. #include <iostream>
    2. #include <type_traits>
    3. int main()
    4. {
    5. constexpr const int* ptr = nullptr; // ptr --> const int* const
    6. std::cout << std::is_same_v <decltype(ptr),const int* const> << std::endl;
    7. }

    Output:

    1. 1

    const

    constexpr VS const

    1. #include<iostream>
    2. using namespace std;
    3. constexpr long int fib(int n)
    4. {
    5. return (n <= 1)? n : fib(n-1) + fib(n-2);
    6. }
    7. int main ()
    8. {
    9. // value of res is computed at compile time.
    10. long int res = fib(30);
    11. cout << res;
    12. return 0;
    13. }

    汇编代码:

    1. main:
    2. push rbp
    3. mov rbp, rsp
    4. sub rsp, 16
    5. mov QWORD PTR [rbp-8], 832040
    6. mov rax, QWORD PTR [rbp-8]
    7. mov rsi, rax
    8. mov edi, OFFSET FLAT:_ZSt4cout
    9. call std::basic_ostream<char, std::char_traits<char> >::operator<<(long)
    10. mov eax, 0
    11. leave
    12. ret
    13. __static_initialization_and_destruction_0(int, int):
    14. push rbp
    15. mov rbp, rsp
    16. sub rsp, 16
    17. mov DWORD PTR [rbp-4], edi
    18. mov DWORD PTR [rbp-8], esi
    19. cmp DWORD PTR [rbp-4], 1
    20. jne .L5
    21. cmp DWORD PTR [rbp-8], 65535
    22. jne .L5
    23. mov edi, OFFSET FLAT:_ZStL8__ioinit
    24. call std::ios_base::Init::Init() [complete object constructor]
    25. mov edx, OFFSET FLAT:__dso_handle
    26. mov esi, OFFSET FLAT:_ZStL8__ioinit
    27. mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
    28. call __cxa_atexit
    29. .L5:
    30. nop
    31. leave
    32. ret
    33. _GLOBAL__sub_I_main:
    34. push rbp
    35. mov rbp, rsp
    36. mov esi, 65535
    37. mov edi, 1
    38. call __static_initialization_and_destruction_0(int, int)
    39. pop rbp
    40. ret
    1. #include<iostream>
    2. using namespace std;
    3. const long int fib(int n)
    4. {
    5. return (n <= 1)? n : fib(n-1) + fib(n-2);
    6. }
    7. int main ()
    8. {
    9. // value of res is computed at compile time.
    10. long int res = fib(30);
    11. cout << res;
    12. return 0;
    13. }

    汇编代码:

    1. fib(int):
    2. push rbp
    3. mov rbp, rsp
    4. push rbx
    5. sub rsp, 24
    6. mov DWORD PTR [rbp-20], edi
    7. cmp DWORD PTR [rbp-20], 1
    8. jg .L2
    9. mov eax, DWORD PTR [rbp-20]
    10. cdqe
    11. jmp .L4
    12. .L2:
    13. mov eax, DWORD PTR [rbp-20]
    14. sub eax, 1
    15. mov edi, eax
    16. call fib(int)
    17. mov rbx, rax
    18. mov eax, DWORD PTR [rbp-20]
    19. sub eax, 2
    20. mov edi, eax
    21. call fib(int)
    22. add rax, rbx
    23. .L4:
    24. mov rbx, QWORD PTR [rbp-8]
    25. leave
    26. ret
    27. main:
    28. push rbp
    29. mov rbp, rsp
    30. sub rsp, 16
    31. mov edi, 30
    32. call fib(int)
    33. mov QWORD PTR [rbp-8], rax
    34. mov rax, QWORD PTR [rbp-8]
    35. mov rsi, rax
    36. mov edi, OFFSET FLAT:_ZSt4cout
    37. call std::basic_ostream<char, std::char_traits<char> >::operator<<(long)
    38. mov eax, 0
    39. leave
    40. ret
    41. __static_initialization_and_destruction_0(int, int):
    42. push rbp
    43. mov rbp, rsp
    44. sub rsp, 16
    45. mov DWORD PTR [rbp-4], edi
    46. mov DWORD PTR [rbp-8], esi
    47. cmp DWORD PTR [rbp-4], 1
    48. jne .L9
    49. cmp DWORD PTR [rbp-8], 65535
    50. jne .L9
    51. mov edi, OFFSET FLAT:_ZStL8__ioinit
    52. call std::ios_base::Init::Init() [complete object constructor]
    53. mov edx, OFFSET FLAT:__dso_handle
    54. mov esi, OFFSET FLAT:_ZStL8__ioinit
    55. mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
    56. call __cxa_atexit
    57. .L9:
    58. nop
    59. leave
    60. ret
    61. _GLOBAL__sub_I_fib(int):
    62. push rbp
    63. mov rbp, rsp
    64. mov esi, 65535
    65. mov edi, 1
    66. call __static_initialization_and_destruction_0(int, int)
    67. pop rbp
    68. ret

    两段代码唯一的不同就是const long int fit(n) --> constexpr long int fit(n)
    从汇编代码看出,第一段代码的汇编代码没有出现fit函数,在编译期编译器就计算fit函数并返回结果,也说明constexpr对应编译期常量。

    两者有不同的用途。 constexpr 主要用于优化,而 const 主要用于实际的常量对象,如 Pi 值。它们都可以应用于成员方法。成员方法是常量,以确保没有意外的变化,由该方法。另一方面,使用 constexpr 的想法是在编译时计算表达式,以便在运行代码时节省时间。const 只能用于非静态成员函数,而 constexpr 可以用于成员函数和非成员函数,甚至可以用于构造函数,但条件是参数和返回类型必须是文本类型。