Appearance
constexpr, consteval, and if constexpr
Modern C++ provides powerful compile-time programming features through constexpr, consteval, and if constexpr. These features enable compile-time evaluation, type-based conditional compilation, and runtime performance optimizations.
constexpr Functions
constexpr functions can be evaluated at compile time if their arguments are compile-time constants. They can also be called at runtime.
cpp
constexpr int factorial(int n) {
return (n <= 1) ? 1 : n * factorial(n - 1);
}
// Compile-time evaluation
constexpr int result = factorial(5); // 120
// Runtime evaluation
int x = 10;
int runtime_result = factorial(x); // Also worksRequirements for constexpr Functions
- Simple operations only: No I/O, dynamic allocation, or complex control flow
- All variables must be initialized: No uninitialized variables
- Recursion depth limited: Compiler-dependent limits
- No side effects: Pure functions
cpp
constexpr int add(int a, int b) {
return a + b; // Simple arithmetic
}
constexpr int max(int a, int b) {
return (a > b) ? a : b; // Conditional expression
}
// This would NOT be constexpr
// constexpr void print(int x) {
// std::cout << x; // I/O not allowed
// }consteval Functions (C++20)
consteval functions are always evaluated at compile time. They cannot be called at runtime.
cpp
consteval int square(int x) {
return x * x;
}
constexpr int result = square(5); // OK: compile-time
// int x = 10;
// int runtime_result = square(x); // ERROR: not constexprUse Cases for consteval
- Compile-time validation: Ensure values are known at compile time
- Template metaprogramming: Force compile-time evaluation
- Performance critical code: Guarantee no runtime overhead
cpp
consteval int validate_size(int size) {
if (size <= 0 || size > 1000) {
throw "Invalid size"; // Compile-time error
}
return size;
}
constexpr int valid_size = validate_size(100); // OK
// constexpr int invalid_size = validate_size(-1); // Compile-time errorif constexpr
if constexpr enables compile-time conditional compilation based on template parameters or type traits.
Basic Syntax
cpp
template<typename T>
auto process_value(T value) {
if constexpr (std::is_integral_v<T>) {
return value * value; // Square integral types
} else if constexpr (std::is_floating_point_v<T>) {
return std::abs(value); // Absolute value for floats
} else {
return value; // Return as-is for other types
}
}Key Features
- Compile-time evaluation: The condition is evaluated at compile time
- Discarded branches: Unused branches are not instantiated
- Type-dependent code: Different code paths for different types
- No runtime overhead: All decisions made at compile time
Implement a function template called 'process_value' that takes a value and performs different operations based on its type using if constexpr. For integral types, return the value squared. For floating-point types, return the absolute value. For pointer types, return the dereferenced value. For all other types, return the value as-is.
cpp
#include <type_traits>
#include <cmath>
#include <string>
// TODO: Implement a function template called 'process_value' that takes a value
// and performs different operations based on its type using if constexpr.
//
// Requirements:
// - For integral types: return the value squared
// - For floating-point types: return the absolute value
// - For pointer types: return the dereferenced value
// - For all other types: return the value as-is
//
// Use if constexpr with type traits like std::is_integral_v, std::is_floating_point_v,
// and std::is_pointer_v to determine the type and perform the appropriate operation.
// Your implementation here