CodeNebula
C++17coreadvanced

constexpr Lambda Expressions

C++17 · Practical Modern · Compiler flag: -std=c++17

Explanation

Lambdas are implicitly `constexpr` when possible, and can be explicitly marked `constexpr`. This allows lambda expressions to be used in compile-time computations, `static_assert`, and template arguments.

Real-World Scenario

Compute compile-time lookup tables or hash values using lambda expressions in a header-only math library.

Quick Snippet

snippet.cpp
constexpr auto square = [](int x) constexpr { return x * x; };
static_assert(square(5) == 25);

constexpr auto factorial = [](int n) constexpr {
    int result = 1;
    for (int i = 2; i <= n; ++i) result *= i;
    return result;
};
static_assert(factorial(6) == 720);

Full Example

constexpr_lambdas.cpp
// CodeNebula - C++17 Example: constexpr Lambda Expressions
// Compile: g++ -std=c++17 -Wall -Wextra -o example constexpr_lambdas.cpp

// Demonstrates C++17 constexpr lambdas. Lambda call operators are implicitly
// constexpr when possible, and can be explicitly marked constexpr. This allows
// lambdas to be used in constant expressions like template arguments and
// static_assert.

#include <iostream>
#include <array>
#include <algorithm>

int main()
{
    // ------------------------------------------------------------------
    // 1. Implicitly constexpr lambda (C++17 makes this automatic)
    // ------------------------------------------------------------------
    auto square = [](int n) { return n * n; };

    // Can be used at compile time:
    constexpr int sq5 = square(5);
    static_assert(sq5 == 25, "5*5 should be 25");

    std::cout << "=== implicitly constexpr ===" << std::endl;
    std::cout << "  square(5) = " << sq5 << " (compile-time)" << std::endl;
    std::cout << "  square(7) = " << square(7) << " (runtime)" << std::endl;

    // ------------------------------------------------------------------
    // 2. Explicitly constexpr lambda
    // ------------------------------------------------------------------
    auto factorial = [](int n) constexpr -> int {
        int result = 1;
        for (int i = 2; i <= n; ++i)
            result *= i;
        return result;
    };

    constexpr int f6 = factorial(6);
    static_assert(f6 == 720, "6! should be 720");

    std::cout << "\n=== explicitly constexpr ===" << std::endl;
    std::cout << "  factorial(6) = " << f6 << std::endl;
    std::cout << "  factorial(10) = " << factorial(10) << std::endl;

    // ------------------------------------------------------------------
    // 3. constexpr lambda as template argument (array size)
    // ------------------------------------------------------------------
    constexpr auto array_size = [](int rows, int cols) { return rows * cols; };
    std::array<int, array_size(3, 4)> grid{};  // 12-element array

    std::cout << "\n=== constexpr lambda as template arg ===" << std::endl;
    std::cout << "  grid size (3*4): " << grid.size() << std::endl;

    // ------------------------------------------------------------------
    // 4. constexpr lambda with captures
    // ------------------------------------------------------------------
    constexpr int base = 10;
    constexpr auto add_base = [base](int x) { return x + base; };
    constexpr int result = add_base(32);
    static_assert(result == 42, "10+32=42");

    std::cout << "\n=== constexpr lambda with capture ===" << std::endl;
    std::cout << "  base=10, add_base(32) = " << result << std::endl;

    // ------------------------------------------------------------------
    // 5. constexpr lambda for compile-time computation
    // ------------------------------------------------------------------
    constexpr auto fibonacci = [](int n) constexpr -> int {
        if (n <= 1) return n;
        int a = 0, b = 1;
        for (int i = 2; i <= n; ++i) {
            int t = a + b;
            a = b;
            b = t;
        }
        return b;
    };

    constexpr int fib10 = fibonacci(10);
    static_assert(fib10 == 55, "fib(10) should be 55");

    std::cout << "\n=== compile-time fibonacci ===" << std::endl;
    for (int i = 0; i <= 10; ++i) {
        std::cout << "  fib(" << i << ") = " << fibonacci(i) << std::endl;
    }

    // ------------------------------------------------------------------
    // 6. Using constexpr lambda at runtime too
    // ------------------------------------------------------------------
    std::cout << "\n=== constexpr lambda at runtime ===" << std::endl;
    int runtime_val = 8;
    std::cout << "  square(" << runtime_val << ") = " << square(runtime_val) << std::endl;

    return 0;
}

/*
Expected output:

=== implicitly constexpr ===
  square(5) = 25 (compile-time)
  square(7) = 49 (runtime)

=== explicitly constexpr ===
  factorial(6) = 720
  factorial(10) = 3628800

=== constexpr lambda as template arg ===
  grid size (3*4): 12

=== constexpr lambda with capture ===
  base=10, add_base(32) = 42

=== compile-time fibonacci ===
  fib(0) = 0
  fib(1) = 1
  fib(2) = 1
  fib(3) = 2
  fib(4) = 3
  fib(5) = 5
  fib(6) = 8
  fib(7) = 13
  fib(8) = 21
  fib(9) = 34
  fib(10) = 55

=== constexpr lambda at runtime ===
  square(8) = 64
*/
Run command
g++ -std=c++17 -Wall -Wextra -o example constexpr_lambdas.cpp