Skip to content

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 works

Requirements for constexpr Functions

  1. Simple operations only: No I/O, dynamic allocation, or complex control flow
  2. All variables must be initialized: No uninitialized variables
  3. Recursion depth limited: Compiler-dependent limits
  4. 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 constexpr

Use 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 error

if 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

  1. Compile-time evaluation: The condition is evaluated at compile time
  2. Discarded branches: Unused branches are not instantiated
  3. Type-dependent code: Different code paths for different types
  4. 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