
- C++ Home
- C++ Overview
- C++ Environment Setup
- C++ Basic Syntax
- C++ Comments
- C++ Hello World
- C++ Omitting Namespace
- C++ Tokens
- C++ Constants/Literals
- C++ Keywords
- C++ Identifiers
- C++ Data Types
- C++ Numeric Data Types
- C++ Character Data Type
- C++ Boolean Data Type
- C++ Variable Types
- C++ Variable Scope
- C++ Multiple Variables
- C++ Basic Input/Output
- C++ Modifier Types
- C++ Storage Classes
- C++ Constexpr Specifier
- C++ Numbers
- C++ Enumeration
- C++ Enum Class
- C++ References
- C++ Date & Time
- C++ Operators
- C++ Arithmetic Operators
- C++ Relational Operators
- C++ Logical Operators
- C++ Bitwise Operators
- C++ Assignment Operators
- C++ sizeof Operator
- C++ Conditional Operator
- C++ Comma Operator
- C++ Member Operators
- C++ Casting Operators
- C++ Pointer Operators
- C++ Operators Precedence
- C++ Unary Operators
- C++ Control Statements
- C++ Decision Making
- C++ if Statement
- C++ if else Statement
- C++ Nested if Statements
- C++ switch Statement
- C++ Nested switch Statements
- C++ Loop Types
- C++ while Loop
- C++ for Loop
- C++ do while Loop
- C++ Foreach Loop
- C++ Nested Loops
- C++ break Statement
- C++ continue Statement
- C++ goto Statement
- C++ Strings
- C++ Strings
- C++ Loop Through a String
- C++ String Length
- C++ String Concatenation
- C++ String Comparison
- C++ Functions
- C++ Functions
- C++ Multiple Function Parameters
- C++ Recursive Function
- C++ Return Values
- C++ Function Overloading
- C++ Function Overriding
- C++ Default Arguments
- C++ Arrays
- C++ Arrays
- C++ Multidimensional Arrays
- C++ Pointer to an Array
- C++ Passing Arrays to Functions
- C++ Return Array from Functions
- C++ Structure & Union
- C++ Structures
- C++ Unions
- C++ Class and Objects
- C++ Object Oriented
- C++ Classes & Objects
- C++ Class Member Functions
- C++ Class Access Modifiers
- C++ Static Class Members
- C++ Static Data Members
- C++ Static Member Function
- C++ Inline Functions
- C++ this Pointer
- C++ Friend Functions
- C++ Pointer to Classes
- C++ Constructors
- C++ Constructor & Destructor
- C++ Default Constructors
- C++ Parameterized Constructors
- C++ Copy Constructor
- C++ Constructor Overloading
- C++ Constructor with Default Arguments
- C++ Delegating Constructors
- C++ Constructor Initialization List
- C++ Dynamic Initialization Using Constructors
- C++ Inheritance
- C++ Inheritance
- C++ Multiple Inheritance
- C++ Multilevel Inheritance
- C++ Object-oriented
- C++ Overloading
- C++ Polymorphism
- C++ Abstraction
- C++ Encapsulation
- C++ Interfaces
- C++ Virtual Function
- C++ Pure Virtual Functions & Abstract Classes
- C++ Design Patterns
- C++ Creational Design Patterns
- C++ File Handling
- C++ Files and Streams
- C++ Reading From File
- C++ Advanced
- C++ Exception Handling
- C++ Dynamic Memory
- C++ Namespaces
- C++ Templates
- C++ Preprocessor
- C++ Signal Handling
- C++ Multithreading
- C++ Web Programming
- C++ Socket Programming
- C++ Concurrency
- C++ Advanced Concepts
- C++ Lambda Expression
- C++ unordered_multiset
C++ Constexpr Specifier
The constexpr specifier was introduced in C++11. It calculates the value of a variable or the return value of a function at compile time. Calculating the values during compilation time improves the performance of the code. It helps in faster execution of programs; since the value is already known during compilation, the runtime overhead is reduced.
Here is a simple practical example that shows how you can calculate the sum of two numbers using constexpr specifier −
#include <iostream> using namespace std; constexpr int sum(int a, int b) { return a + b; } int main() { constexpr int x = sum(10, 2); cout << "10 + 2 = " << x << endl; return 0; }
The output of the above code is as follows −
10 + 2 = 12
The above example was a simple implementation of constexpr specifier. In this chapter, we will discuss following topics in detail −
- Need of Constexpr Specifier
- Declaring Variables with Constexpr
- Functions with Constexpr Specifier
- Constructors with Constexpr Specifier
- Use Cases of Constexpr Specifier
- Difference Between Const and Constexpr
- Limitations of Constexpr Specifier
Why We Need constexpr in C++?
The constexpr is needed in C++ because of the following reasons −
- Performance optimization − All the overhead of calculating mathematical expressions has now shifted from runtime to compile time. This helps in faster execution of program, as no computation is needed at runtime.
- Better compile-time constants − Features such as array size, switch case labels, and template parameters require value during compilation which can be provided using constexpr specifier, but there is no guarantee with the const keyword.
Declaring Variables with constexpr
You can use a constexpr specifier to declare a variable. The rule for declaring a variable with constexpr is that it should be initialized with a constant expression.
The valid and invalid syntax for a constexpr variable is given below −
// -----Valid declarations----- (1) constexpr int y = 4; (2) constexpr int z = 7; constexpr int x = z; // -----Invalid declarations----- (1) constexpr int a; // constexpr variable must be initialized (2) constexpr int b = x + 2; // x is not a constant expression (3) int z = 7; constexpr int x = z; // z is not a constant expression
Example: Valid Declaration
Below is an example to demonstrate the use of constexpr variable −
#include <iostream> using namespace std; int main() { constexpr int z = 7; constexpr int x = z; cout << "value of z: " << z << endl; cout << "value of x: " << x << endl; return 0; }
The output of the above code is given below −
value of z: 7 value of x: 7
Example: Invalid Declaration
Here is an example of an invalid declaration of constexpr variable −
#include <iostream> using namespace std; int main() { int z = 7; constexpr int x = z; cout << "value of z: " << z << endl; cout << "value of x: " << x << endl; return 0; }
The output of the above code is given below. Here, we can see the error as int z is not a constant.
main.cpp: In function 'int main()': main.cpp:6:23: error: the value of 'z' is not usable in a constant expression 6 | constexpr int x = z; | ^ main.cpp:5:9: note: 'int z' is not const 5 | int z = 7; | ^
Functions with Constexpr Specifier
You can use constexpr specifier to define a function. Given below are the rules that you should follow while using constexpr to define a function:
- The parameter and return type of function should be of literal type. For example: int, double, char, and bool are valid, whereas string and vector are invalid return types.
- The function should have only one return statement till C++11. From C++14 onwards, it can have multiple return statements.
- The function cannot have any side effects. For example: the function can not have input/output (cin/cout) operations, can not modify global and static variables.
- A constexpr function can only call other constexpr functions, not simple functions.
Factorial Calculation with Constexpr
Here is an example to implement a function with constexpr specifier to calculate the factorial of 5 at compile-time −
#include <iostream> using namespace std; constexpr long long factorial(int n) { return (n <= 1) ? 1 : n * factorial(n - 1); } int main() { // Computed at compile-time constexpr long long fact = factorial(5); cout << "Factorial of 5 = " << fact << endl; return 0; }
The output of the above code is given below −
Factorial of 5 = 120
Fibonacci Calculation with Constexpr
Below is an example to find the fibonacci number. In this example, multiple return statements are used which was not valid before C++14.
#include <iostream> using namespace std; // Fibonacci using iteration constexpr long long fibonacci(int n) { if (n <= 1) return n; long long prev = 0, curr = 1; for (int i = 2; i <= n; ++i) { long long next = prev + curr; prev = curr; curr = next; } return curr; } int main() { // Computed at compile-time constexpr long long fib = fibonacci(10); cout << "Fibonacci of 10 = " << fib << endl; return 0; }
The output of the above code is given below −
Fibonacci of 10 = 55
Constructors with Constexpr Specifier
The constexpr specifier can be used to create classes, objects, and declare a constructor. To declare a constructor, we must follow rules given below −
- You can only use literal type parameters in a constructor.
- You cannot have virtual base classes or virtual member functions in classes.
- Only other constexpr constructors and functions can be called.
- Classes with constexpr constructors cannot have mutable data members.
- A constructor with constexpr cannot have try/catch blocks or throw expressions.
The example below demonstrates how to use constexpr specifier with a constructor for converting a temperature given in celsius to fahrenheit −
#include <iostream> using namespace std; class Temperature { double celsius; public: // constexpr constructor constexpr Temperature(double c) : celsius(c) {} // convert to Fahrenheit constexpr double toFahrenheit() const { return (celsius * 9.0 / 5.0) + 32.0; } // get Celsius constexpr double getCelsius() const { return celsius; } }; int main(){ // Compile-time constant object constexpr Temperature t1(25.0); cout << "Compile time temperature conversions:" << endl; // computed at compile-time constexpr double f1 = t1.toFahrenheit(); cout << t1.getCelsius() << "°C = " << f1 << "°F" << endl; // Runtime object double input = 100.0; Temperature t2(input); // runtime cout << "\nRuntime temperature conversions:" << endl; cout << t2.getCelsius() << "°C = " << t2.toFahrenheit() << "°F" << endl; return 0; }
The output of the above code is as follows −
Compile time temperature conversions: 25°C = 77°F Runtime temperature conversions: 100°C = 212°F
Use Cases of Constexpr Specifier
There are multiple practical use cases of constexpr specifier in various scenarios. Here are some of the uses mentioned below −
Array Sizes at Compile-time
In this example, constexpr provides the array size to print the numbers at compile-time using bufferSize() function.
#include <iostream> using namespace std; constexpr int bufferSize() { return 10; } int main() { // valid as the buffer size is // known at compile-time int arr[bufferSize()]; for (int i = 0; i < bufferSize(); i++){ arr[i] = i; } cout << "Array elements: "; for (int i = 0; i < bufferSize(); i++) { cout << arr[i] << " "; } cout << endl; return 0; }
The output of the above code is given below −
Array elements: 0 1 2 3 4 5 6 7 8 9
Mathematical Computations
The following example calculates the 10th power of 2. The power is calculated at compile-time rather than run-time. This reduces the run-time overhead of the program.
#include <iostream> using namespace std; constexpr long long power(int base, int exp) { long long result = 1; for (int i = 0; i < exp; ++i) { result *= base; } return result; } int main() { constexpr long long n = power(2, 10); // Calculated at compile time cout << "2^10 = " << n << endl; return 0; }
The output of the above code is given below −
2^10 = 1024
Using Constexpr in Switch Case Labels
Here is an example to pre-define the label of switch case using constexpr specifier. If we remove constexpr, it will give an error.
#include <iostream> using namespace std; constexpr int choice() { return 3; // value known at compile-time } int main() { switch (3) { case choice(): cout << "Matched case 3" << endl; break; default: cout << "Default case" << endl; } return 0; }
The output of the above code is given below −
Matched case 3
Difference between Const and Constexpr
The following table lists the differences between const keyword and constexpr specifier −
const | constexpr |
---|---|
Values can be evaluated at compile-time or run-time. | Values must be evaluated at compile time. |
The variables can be initialized with compile time or run time values. | We initialize variables with compile-time constant expressions only. |
There may be runtime overhead. | There is zero runtime overhead as evaluation occurs at compile-time. |
Limitations of Constexpr Specifier
There are various advantages of using a constexpr specifier such as reducing the runtime overhead and increasing the performance but there are some limitations too that are mentioned below:
- The input and output operations (cin/cout) cannot be used in the functions declared with constexpr specifier.
- It does not support dynamic memory allocation.
- One constexpr function can only call another constexpr function. We can not call any other normal functions.
- There is no support for exception handling.
- Function parameters or return type can only be of literal type.
Conclusion
In this chapter, we explained the use of constexpr specifier in detail. It calculates the value at compile-time, reducing the run-time overhead. We discussed the rules to use constexpr with variables, functions, and constructors with code examples. It offers the advantage of increasing the performance, but it also has some limitations that we highlighted at the end of the chapter.