본문 바로가기

Programming/C++ Basic

[Basic C++] 24 - "const" and "constexpr" keywords in C++

1. "const" and "constexpr" keywords

There are two keywords in C++ to specify the value of a variable or function to be constant; there are "const" and "constexpr" keywords in C++. Lots of basic-level programmers are likely to think that "the sole purpose of using these keywords is to prevent some variables from being altered accidentally".

 

When it comes to "const" specifier, the statement is true; however "constexpr" specifier was born to force the compiler to compute constexpr variables during the compilation for reducing the execution time of a program.

 

A proper understanding of these keywords is essential in modern programming to handle variables and functions with better efficiency.

 

#include <iostream>

constexpr int var1 = 1;
const int var2 = 2;
constexpr const int var3 = 3;
int var4 = 4;

int main() {
	int ary1[var1];
	int ary2[var2];
	int ary3[var3];
	int ary4[var4]; // Error 2131  failure was caused by 
    			//non-constant arguments or reference to a non-constant symbol
}

 

The preceding example shows three const or constexpr integer variables and one non-const integer variable. Four arrays are defined within the main function, but "ary4" does not compile, as its size is not const (Error 2131). Both keywords define constant variables and the variables are not modifiable. Then let's see the real difference of these two keywords.


2. "constexpr" specifier

"constexpr" specifier is the abbreviation of "constant expression". The purpose of this keyword is to specify variables or functions to be evaluated during the compile time. In other words, "constexpr" guarantees that such variables and functions are constant, whereas in some cases "const" variables are not actually constant.

 

Figure 1 const and constexpr keywords

The preceding example shows two variables. "n" is an integer variable with the value of 50. "s1" is a const integer variable with the value of "n" which is non-const. "s2" is a constexpr variable with the value of "n". As "const" does not guarantee compile-time evaluation, "s1" is considered valid by the compiler. On the other hand, as "s2" is a constexpr variable, the compiler raises an error "C2131".

 

Knowing the errors of a program during the compilation gives a huge advantage to programmers to correct their programs. In contrast to compile errors, runtime errors often cost the programmer a longer period of time to find. Therefore, it is advisable to use "constexpr" rather than "const" to guarantee variables or functions are evaluated during the compilation.

 

2.1) "constexpr" functions

"constexpr" specifier can also be used for functions and the execution time of such functions show shorter execution time rather than ordinary functions. The definition of a constexpr function can only have the return statement, and o void return type (no return value) is allowed. The execution time of programs can be easily measured by "chrono" library in C++.

 

#include <iostream>
#include <chrono>

int func1(int a, int b) {
	return (a < b)? a*b:a/b;
}
int main() {

	int a = 100;
	int b = 200;
	auto start = std::chrono::high_resolution_clock::now();
	std::cout << func1(a, b) << std::endl;
	auto end = std::chrono::high_resolution_clock::now();
	std::chrono::duration<double, std::milli> ms = end - start;
	std::cout << ms.count();
}
#include <iostream>
#include <chrono>

constexpr int func1(int a, int b) {
	return (a < b)? a*b:a/b;
}
int main() {

	int a = 100;
	int b = 200;
	auto start = std::chrono::high_resolution_clock::now();
	std::cout << func1(a, b) << std::endl;
	auto end = std::chrono::high_resolution_clock::now();
	std::chrono::duration<double, std::milli> ms = end - start;
	std::cout << ms.count();
}

 

The preceding example shows a function with "constexpr" specifier and a function with no specifier. The total execution time of each code was "0.75 sec" and "0.45 sec" respectively. Obviously, the function with constexpr specifier needs less time to be executed.

 

2.2) "constexpr" classes

The "constexpr" specifier can also modify classes which consist of literaltype members variables only. When explicit constructors are defined, the constructors must be "constexpr". Implicit constructors are "constexpr" by default. In addition, "constexpr" class must follow the below conditions.

 

  • The parameters of such constructors must be literaltype.
  • The body of such constructor must be empty
  • "constexpr" class can not have virtual base classes.
  • Base class and non-static members must be initialized by member initializers.
  • "constexpr" member functions are implicitly "const", thus no "const" is needed explicitly.
    • virtual member functions can not be "constexpr" (because they are evaluated during runtime).

2.3) more conditions for "constexpr" specifier

The following contents are mainly from cppreference.com [1]. A constexpr variable must satisfy the following conditions.

 

[1] Its type must be a literaltype.

: A literal type is

  • a scalar type,
  • reference type,
  • an array of literal type,
  • a class type that has all of the following properties:
    • it has a trivial destructor,
    • every constructor call and full-expression in the brace-or-equal-initializers (default member initializers) for non-static members is a constant expression.
    • all of its non-static data members and base classes are of non-volatile literal types.

[2] It must be initialized instantly.

: It must be declared and defined with an initializer at the same time.

 

[3] The full expression of its initialization must be a const expression.

: It must be evaluated as const during the compilation.


4. REFERENCE

[1] en.cppreference.com/w/cpp/language/constexpr