constexpr if
constexpr if is a new syntax introduced in C++17, allowing compile-time selection based on conditions, thus avoiding the need for template specialization and SFINAE techniques often used in template metaprogramming.
SFINAE stands for “Substitution Failure Is Not An Error,” which is a technique in C++ template metaprogramming. When instantiating templates, if there is an error in deducing template parameters or performing template specialization, SFINAE allows the compiler to continue template instantiation rather than reporting an error.
However, this feature can make your code difficult to read. Let’s look at some code examples.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Define a function template specialized based on the type T, for handling integer data
template<typename T>
void process_data(const T& data) {
std::cout << "Unknown type of data" << std::endl;
}
// Template specialization for handling integer data
template<>
void process_data<int>(const int& data) {
std::cout << "Integer data: " << data << std::endl;
}
// Template specialization for handling floating-point data
template<>
void process_data<float>(const float& data) {
std::cout << "Floating-point data: " << data << std::endl;
}
// Template specialization for handling string data
template<>
void process_data<std::string>(const std::string& data) {
std::cout << "String data: " << data << std::endl;
}
int main() {
std::string str = "Hello";
process_data(10); // Integer data
process_data(3.14f); // Floating-point data
process_data(str); // String data
process_data(true); // Unknown type of data
return 0;
}
Using constexpr if
The code becomes much simpler and easier to read
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Define a function template that performs different operations based on the type T at compile time
template<typename T>
void process_data(const T& data) {
// Use constexpr if to choose based on the type T at compile time
if constexpr (std::is_integral<T>::value) {
std::cout << "Integer data: " << data << std::endl;
}
else if constexpr (std::is_floating_point<T>::value) {
std::cout << "Floating-point data: " << data << std::endl;
}
else if constexpr (std::is_same<T, std::string>::value) {
std::cout << "String data: " << data << std::endl;
}
else {
std::cout << "Unknown type of data" << std::endl;
}
}
int main() {
std::string str = "Hello";
process_data(10); // Integer data
process_data(3.14f); // Floating-point data
process_data(str); // String data
process_data(true); // Unknown type of data
return 0;
}
Result
In this example, the reason why the output of process_data(true) is “unknown type data” in the version using template specialization, while it is “integer type data” in the version using constexpr
if, is because these two methods handle conditionals differently.
In the version using template specialization, we have defined specialized versions for integer, floating-point, and string types, but not for boolean type. Therefore, when process_data(true) is called, the compiler looks for a specialization related to boolean type but cannot find a corresponding one, so it uses the generic template version, resulting in “unknown type data” output.
On the other hand, in the constexpr
if version, we use if constexpr
for conditional evaluation, choosing different code paths based on the condition. In this case, the condition is std::is_integral
Copy the code and run it to see the results! This will provide a clearer understanding of what was discussed above. Happy coding!