c++

openstd cpp standards.

Source files of most examples is available here.

Type deduction

Force compile error to see what auto is deduced to.

auto foo = bar();

// force compile error
typename decltype(foo)::_;

Strict aliasing and type punning

The strict aliasing rules describe via which alias a value can be accessed.

Informal: an alias is a reference / pointer to a value.

Accessing a value through an alias that violates the strict aliasing rules is undefined behavior (UB).

Examples below on godbolt.

int i = 0;

// Valid aliasing (signed / unsigned type).
*reinterpret_cast<signed int*>(&i);
*reinterpret_cast<unsigned int*>(&i);

// Valid aliasing (cv qualified type).
*reinterpret_cast<const int*>(&i);
*reinterpret_cast<const unsigned*>(&i);

// Valid aliasing (byte type).
*reinterpret_cast<char*>(&i);
*reinterpret_cast<std::byte*>(&i);

// Invalid aliasing, dereferencing pointer is UB.
*reinterpret_cast<short*>(&i);
*reinterpret_cast<float*>(&i);

NOTE: Casting pointer to invalid aliasing type is not directly UB, but dereferencing the pointer is UB.

short s[2] = { 1, 2 };

// Invalid aliasing (UB) - type punning, UB to deref ptr (int has stricter
// alignment requirements than short).
*reinterpret_cast<int*>(s);


// Arbitrary byte pointer.
char c[4] = { 1, 2, 3, 4 };

// Invalid aliasing (UB) - type punning, UB to deref ptr (int has stricter
// alignment requirements than char).
*reinterpret_cast<int*>(c);

At the time of writing, the current c++ std draft contains the following.

If a program attempts to access the stored value of an object through a glvalue
whose type is not **similar** (7.3.6) to one of the following types the
behavior is undefined [44]

(11.1) the dynamic type of the object,
(11.2) a type that is the signed or unsigned type corresponding to the dynamic
       type of the object, or
(11.3) a char, unsigned char, or std::byte type.

[44]: The intent of this list is to specify those circumstances in which an
      object can or cannot be aliased.

The paragraph is short but one also needs to understand the meaning of similar (similar_types).

This paragraph is actually somewhat more explicit in the c++17 std.

If a program attempts to access the stored value of an object through a glvalue
of other than one of the following types the behavior is undefined [63]

(11.1) the dynamic type of the object,
(11.2) a cv-qualified version of the dynamic type of the object,
(11.3) a type similar (as defined in 7.5) to the dynamic type of the object,
(11.4) a type that is the signed or unsigned type corresponding to the dynamic
       type of the object,
(11.5) a type that is the signed or unsigned type corresponding to a
       cv-qualified version of the dynamic type of the object,
(11.6) an aggregate or union type that includes one of the aforementioned types
       among its elements or non- static data members (including, recursively,
       an element or non-static data member of a subaggregate or contained
       union),
(11.7) a type that is a (possibly cv-qualified) base class type of the dynamic
       type of the object,
(11.8) a char, unsigned char, or std::byte type.

[63]: The intent of this list is to specify those circumstances in which an
      object may or may not be aliased.

Additional references:

  • What is the Strict Aliasing Rule and Why do we care

    The article shows a small example how the compiler may optimized using the strict aliasing rules.

    int alias(int* i, char* c) {
      *i = 1;
      *c = 'a';  // char* may alias int*
      return *i;
    }
    
    int noalias(int* i, short* s) {
        *i = 1;
        *s = 2;  // short* does not alias int*
        return *i;
    }
    
    alias(int*, char*):
    mov    DWORD PTR [rdi] ,0x1  ; *i = 1;
    mov    BYTE PTR [rsi], 0x61  ; *c = 'a';
    mov    eax,DWORD PTR [rdi]   ; Must reload, char* can alias int*.
    ret
    
    noalias(int*, short*):
    mov    DWORD PTR [rdi], 0x1  ; *i = 1;
    mov    WORD PTR [rsi], 0x2   ; *s = 2;
    mov    eax,0x1               ; Must not reload, short* can not alias int*.
    ret
    
  • reinterpret_cast type aliasing

    1. Any object pointer type T1* can be converted to another object pointer type cv T2*. This is exactly equivalent to static_cast<cv T2*>(static_cast<cv void*>(expression)) (which implies that if T2's alignment requirement is not stricter than T1's, the value of the pointer does not change and conversion of the resulting pointer back to its original type yields the original value). In any case, the resulting pointer may only be dereferenced safely if allowed by the type aliasing rules (see below).
    int I;
    char* X = reinterpret_cast<char*>(&I);  // Valid, char allowed to alias int.
    *X = 42;
    int* Y = reinterpret_cast<int*>(X);     // Cast back to original type.
    *Y = 1337;  // safe
    
    char C[4];
    int* P = reinterpret_cast<int*>(C);     // Cast is ok, not yet UB.
    *P = 1337; // UB, violates strict aliasing / alignment rules.
               // https://stackoverflow.com/questions/52492229/c-byte-array-to-int
    
  • On gcc strict aliasing is enabled starting with -O2.

    for i in {0..3} g s; do echo "-O$i $(g++ -Q --help=optimizers -O$i | grep fstrict-aliasing)"; done
    -O0   -fstrict-aliasing           [disabled]
    -O1   -fstrict-aliasing           [disabled]
    -O2   -fstrict-aliasing           [enabled]
    -O3   -fstrict-aliasing           [enabled]
    -Og   -fstrict-aliasing           [disabled]
    -Os   -fstrict-aliasing           [enabled]
    

__restrict keyword

The __restrict keyword allows the programmer to tell the compiler that two pointer will not alias each other.

int alias(int* a, int* b) {
    *a = 1;
    *b = 2;
    return *a;
}

// alias(int*, int*):                           # @alias(int*, int*)
//         mov     dword ptr [rdi], 1
//         mov     dword ptr [rsi], 2
//         mov     eax, dword ptr [rdi]
//         ret

int noalias(int* __restrict a, int* __restrict b) {
    *a = 1;
    *b = 2;
    return *a;
}

// noalias(int*, int*):                         # @noalias(int*, int*)
//         mov     dword ptr [rdi], 1
//         mov     dword ptr [rsi], 2
//         mov     eax, 1
//         ret

However this should only be used with care and in a narrow scope, as it is easy to violate self defined contract, see godbolt.

Type punning

The correct way to do type-punning in c++:

  1. std::bit_cast (c++20)
  2. std::memcpy

Variadic templates (parameter pack)

#include <iostream>

// -- Example 1 - print template value arguments.

// Base case with one parameter.
template<int P>
void show_int() {
    printf("%d\n", P);
}

// General case with at least two parameters, to disambiguate from base case.
template<int P0, int P1, int... Params>
void show_int() {
    printf("%d, ", P0);
    show_int<P1, Params...>();
}

// -- Example 2 - print values of different types.

// Base case with one parameter.
template<typename T>
void show(const T& t) {
    std::cout << t << '\n';
}

// General case with at least two parameters, to disambiguate from base case.
template<typename T0, typename T1, typename... Types>
void show(const T0& t0, const T1& t1, const Types&... types) {
    std::cout << t0 << ", ";
    show(t1, types...);
}

int main() {
    show_int<1, 2, 3, 4, 5>();
    show(1, 1.0, "foo", 'a');
}

Forwarding reference (fwd ref)

A forwarding reference is a special references that preserves the value category of a function parameter and therefore allows for perfect forwarding.

A forwarding reference is a parameter of a function template, which is declared as rvalue reference to a non-cv qualified type template parameter.

template<typename T>
void fn(T&& param); // param is a forwarding reference

Perfect forwarding can be achieved with std::forward. This for example allows a wrapper function to pass a parameter with the exact same value category to a down-stream function which is being invoked in the wrapper.

#include <cstdio>
#include <utility>

struct M {};

// -- CONSUMER -----------------------------------------------------------------

void use(M&) {
    puts(__PRETTY_FUNCTION__);
}

void use(M&&) {
    puts(__PRETTY_FUNCTION__);
}

// -- TESTER -------------------------------------------------------------------

template<typename T>
void wrapper(T&& param) {  // forwarding reference
    puts(__PRETTY_FUNCTION__);
    // PARAM is an lvalue, therefore this always calls use(M&).
    use(param);
}

template<typename T>
void fwd_wrapper(T&& param) {  // forwarding reference
    puts(__PRETTY_FUNCTION__);
    // PARAM is an lvalue, but std::forward returns PARAM with the same value
    // category as the forwarding reference takes.
    use(std::forward<T>(param));
}

// -- MAIN ---------------------------------------------------------------------

int main() {
    {
        std::puts("==> wrapper rvalue reference");
        wrapper(M{});
        // calls use(M&).

        std::puts("==> wrapper lvalue reference");
        struct M m;
        wrapper(m);
        // calls use(M&).
    }
    {
        std::puts("==> fwd_wrapper rvalue reference");
        fwd_wrapper(M{});
        // calls use(M&&).

        std::puts("==> fwd_wrapper lvalue reference");
        struct M m;
        fwd_wrapper(m);
        // calls use(M&).
    }
}

Example: any_of template meta function

#include <type_traits>

template<typename T, typename... U>
struct any_of : std::false_type {};

// Found our type T in the list of types U.
template<typename T, typename... U>
struct any_of<T, T, U...> : std::true_type {};

// Pop off the first element in the list of types U,
// since it didn't match our type T.
template<typename T, typename U0, typename... U>
struct any_of<T, U0, U...> : any_of<T, U...> {};

// Convenience template variable to invoke meta function.
template<typename T, typename... U>
constexpr bool any_of_v = any_of<T, U...>::value;

static_assert(any_of_v<int, char, bool, int>, "");
static_assert(!any_of_v<int, char, bool, float>, "");

Example: SFINAE (enable_if)

Provide a single entry point Invoke to call some Operations. Use enable_if to enable/disable the template functions depending on the two available traits an operation can have:

  • Operation returns a result
  • Operation requires a context
#include <iostream>
#include <type_traits>

// Helper meta fns.

template<typename T>
using enable_if_bool = std::enable_if_t<T::value, bool>;

template<typename T>
using disable_if_bool = std::enable_if_t<!T::value, bool>;

template<typename T>
using has_dst = std::integral_constant<bool, !std::is_same<typename T::Return, void>::value>;

// Template meta programming invoke machinery.

namespace impl {
    // Invoke an OPERATION which *USES* a context.
    template<typename Ctx, template<typename> class Op, typename... P,
             enable_if_bool<typename Op<Ctx>::HasCtx> = true>
    typename Op<Ctx>::Return Invoke(const Ctx& C, P... params) {
        return Op<Ctx>()(C, params...);
    }

    // Invoke an OPERATION which uses *NO* context.
    template<typename Ctx, template<typename> class Op, typename... P,
             disable_if_bool<typename Op<Ctx>::HasCtx> = true>
    typename Op<Ctx>::Return Invoke(const Ctx&, P... params) {
        return Op<Ctx>()(params...);
    }
}  // namespace impl

// Invoke an OPERATION which *HAS* a DESTINATION with arbitrary number of arguments.
template<typename Ctx, template<typename> class Op, typename... P,
         enable_if_bool<has_dst<Op<Ctx>>> = true>
void Invoke(const Ctx& C, P... params) {
    std::cout << "Invoke " << Op<Ctx>::Name << '\n';
    typename Op<Ctx>::Return R = impl::Invoke<Ctx, Op>(C, params...);
    std::cout << "returned -> " << R << '\n';
}

// Invoke an OPERATION which has *NOT* a DESTINATION with arbitrary number of arguments.
template<typename Ctx, template<typename> class Op, typename... P,
         disable_if_bool<has_dst<Op<Ctx>>> = true>
void Invoke(const Ctx& C, P... params) {
    std::cout << "Invoke " << Op<Ctx>::Name << " without destination." << '\n';
    impl::Invoke<Ctx, Op>(C, params...);
}

// Custom context.

struct Ctx {
    void out(const char* s, unsigned v) const {
        printf("%s%x\n", s, v);
    }
};

// Operations to invoke.

template<typename Ctx>
struct OpA {
    using HasCtx = std::false_type;
    using Return = int;
    static constexpr const char* const Name = "OpA";

    constexpr Return operator()(int a, int b) const {
        return a + b;
    }
};

template<typename Ctx>
struct OpB {
    using HasCtx = std::true_type;
    using Return = void;
    static constexpr const char* const Name = "OpB";

    Return operator()(const Ctx& C, unsigned a) const {
        C.out("a = ", a);
    }
};

int main() {
    Ctx C;

    Invoke<Ctx, OpA>(C, 1, 2);
    Invoke<Ctx, OpB>(C, 0xf00du);

    return 0;
}

Example: Minimal templatized test registry

A small test function registry bringing together a few different template features.

#include <cstdio>
#include <functional>
#include <map>
#include <string>
#include <type_traits>

template<typename R, typename... P>
struct registry {
    using FUNC = R (*)(P...);
    using SELF = registry<R, P...>;
    using RET = R;

    static SELF& get() {
        static SELF r;
        return r;
    }

    bool add(std::string nm, FUNC fn) {
        const auto r = m_fns.insert({std::move(nm), std::move(fn)});
        return r.second;
    }

    R invoke(const std::string& nm, P... p) const {
        return invoke_impl<R>(nm, p...);
    }

    void dump() const {
        for (const auto& it : m_fns) {
            std::puts(it.first.c_str());
        }
    }

  private:
    std::map<std::string, FUNC> m_fns;

    template<typename RET>
    std::enable_if_t<std::is_same_v<RET, void>> invoke_impl(const std::string& nm, P... p) const {
        const auto it = m_fns.find(nm);
        if (it == m_fns.end()) {
            return;
        }
        std::invoke(it->second, p...);
    }

    template<typename RET>
    std::enable_if_t<!std::is_same_v<RET, void>, RET> invoke_impl(const std::string& nm,
                                                                  P... p) const {
        const auto it = m_fns.find(nm);
        if (it == m_fns.end()) {
            static_assert(std::is_default_constructible_v<RET>,
                          "RET must be default constructible");
            return {};
        }
        return std::invoke(it->second, p...);
    }
};

#define TEST_REGISTER(REGISTRY, NAME)                                                      \
    static bool regfn_##REGISTRY##NAME() {                                                 \
        const bool r = REGISTRY::get().add(#NAME, NAME);                                   \
        if (!r) {                                                                          \
            std::puts("Failed to register test " #NAME ", same name already registered!"); \
            std::abort();                                                                  \
        }                                                                                  \
        return r;                                                                          \
    }                                                                                      \
    static const bool reg_##REGISTRY##NAME = regfn_##REGISTRY##NAME();

#define TEST(REGISTRY, NAME, ...)    \
    REGISTRY::RET NAME(__VA_ARGS__); \
    TEST_REGISTER(REGISTRY, NAME);   \
    REGISTRY::RET NAME(__VA_ARGS__)

// -- Usage 1 simple usage.

using REG1 = registry<void>;
TEST(REG1, test1) {
    std::puts("REG1::test1");
}
TEST(REG1, test2) {
    std::puts("REG1::test2");
}

// -- Usage 2 with convenience macro wrapper.

using REG2 = registry<void, bool>;
#define TEST2(NAME, ...) TEST(REG2, NAME, ##__VA_ARGS__)

TEST2(test1, bool val) {
    printf("REG2::test1 val %d\n", val);
}

int main() {
    const auto& R1 = REG1::get();
    R1.dump();
    R1.invoke("test1");
    R1.invoke("test2");

    const auto& R2 = REG2::get();
    R2.dump();
    R2.invoke("test1", true);

    return 0;
}

Example: Concepts pre c++20

Prior to c++20's concepts, SFINAE and std::void_t can be leveraged to build something similar allowing to define an interface (aka trait) for a template parameter.


template<typename T, template<typename> class Checker, typename = void>
struct is_valid : std::false_type {};

template<typename T, template<typename> class Checker>
struct is_valid<T, Checker, std::void_t<Checker<T>>> : std::true_type {};

template<typename T, template<typename> class Checker>
static constexpr bool is_valid_v = is_valid<T, Checker>::value;

// -----------------------------------------------------------------------------

template<typename T, typename R, template<typename> class Checker, typename = void>
struct is_valid_with_ret : std::false_type {};

template<typename T, typename R, template<typename> class Checker>
struct is_valid_with_ret<T, R, Checker, std::void_t<Checker<T>>> : std::is_same<R, Checker<T>> {};

template<typename T, typename R, template<typename> class Checker>
static constexpr bool is_valid_with_ret_v = is_valid_with_ret<T, R, Checker>::value;

// -----------------------------------------------------------------------------

template<typename T>
struct is_entry {
    template<typename TT>
    using init = decltype(std::declval<TT>().init());
    template<typename TT>
    using tag = decltype(std::declval<TT>().tag());
    template<typename TT>
    using val = decltype(std::declval<TT>().val());

    static constexpr bool value = is_valid_v<T, init> && is_valid_with_ret_v<T, int, tag> &&
                                  is_valid_with_ret_v<T, typename T::Type, val>;
};

template<typename T>
static constexpr bool is_entry_v = is_entry<T>::value;

template<typename E>
struct Entry {
    using Type = E;
    void init();
    int tag() const;
    E val() const;
};

int main() {
    static_assert(is_entry_v<Entry<bool>>, "");
}

The main mechanic can be explained with the following reduced example. If one of the decltype(std:declval<T>... expressions is ill-formed, the template specialization for is_valid will be removed from the candidate set due to SFINAE.

#include <type_traits>

// (1) Primary template.
template<typename T, typename = void>
struct is_valid : std::false_type {};

// (2) Partial template specialization.
template<typename T>
struct is_valid<T, std::void_t<decltype(std::declval<T>().some_fun1()),
                               decltype(std::declval<T>().some_fun2())>> : std::true_type {};
struct A {
    void some_fun1() {}
    void some_fun2() {}
};

struct B {};

static_assert(is_valid<A>::value, "is true");
// * Compare template arg list with primary template, we only supplied one
//   arg, the second one will be defaulted as
//   is_valid<A, void>
// * Compare template arg list against available specializations, this will
//   try to match the pattern <A, void> against the patterns defined in the
//   partial specializations.
// * Try specialization (2)
//   * T -> A
//   * Evaluate std::void_t -> decltype's are well-formed
//     std::void_t<...> -> void
//   * Specialization (2) matches <A, void>
// * Pick the most specialized version -> (2)

static_assert(!is_valid<B>::value, "is false");
// * Compare template arg list with primary template, we only supplied one
//   arg, the second one will be defaulted as
//   is_valid<A, void>
// * Compare template arg list against available specializations, this will
//   try to match the pattern <B, void> against the patterns defined in the
//   partial specializations.
// * Try specialization (2)
//   * T -> B
//   * Evaluate std::void_t -> decltype's are ill-formed
//   * Specialization (2) is removed from candidate set, no hard error (SFINAE)
// * No specialization matches, take the primary template.

std::declval<T>() creates an instance of type T in an unevaluated context.

A more detailed description is available in the SO discussion How does void_t work.

Example: Concepts since c++20

// REQUIRES EXPRESSION
//   requires { requirement-seq }
//   requires ( parameter-list ) { requirement-seq }
//
// [1] https://en.cppreference.com/w/cpp/language/requires
// [2] https://en.cppreference.com/w/cpp/language/constraints#Constraints
//
// REQUIREMENT CLAUSE
//   Not the same as a REQUIREMENT EXPRESSIONS, and is used to require
//   constraints (express concept bounds).
//
// [1] https://en.cppreference.com/w/cpp/language/constraints#Requires_clauses

// -- HELPER -------------------------------------------------------------------

template<typename T>
using Alias = T;

void print(int);

// -- CONCEPTS & REQUIRE EXPRESSIONS -------------------------------------------

// Simple concept from a type trait.
template<typename T, typename U>
concept Same = std::is_same<T, U>::value;

// Simple requirement concepts.
template<typename T>
concept TraitAddAndPrint = requires(T t, int i) {
    // Adding T + int must be supported.
    t + i;
    // Calling print(T) must be available.
    print(t);
};

// Type requirement concepts.
template<typename T>
concept TraitTypes = requires(T t) {
    // T must have a type definition inner.
    typename T::inner;
    // Type alias must exist.
    typename Alias<T>;
};

// Compound requirement concepts.
template<typename T>
concept TraitFns = requires(T t, const T c) {
    // void T::foo() must exist.
    { t.foo() };
    // bool T::bar() const; must exist.
    { c.bar() } -> Same<bool>;
    // static void T::stat(); must exist.
    { T::stat() } -> Same<int>;
};

// Nested requirement concepts.
template<typename T>
concept TraitNested = requires(T t) {
    // Must satisfy other concepts.
    requires TraitTypes<T>;
    requires TraitFns<T>;
};

// -- REQUIRE EXPRESSIONS ------------------------------------------------------

// Require expressions can be evaluated to booleans.
template<typename T>
static constexpr bool IsTraitFns = requires { requires TraitFns<T>; };

// Require expressions can also be used in static assertions.
static_assert(requires { requires Same<int, int>; });
static_assert(!requires {
    typename Alias<int>;
    requires Same<int, void>;
});

// -- TESTS --------------------------------------------------------------------

static_assert(requires { requires TraitAddAndPrint<int>; });

struct FnTypeGood {
    using inner = int;
};
struct FnTypeBad {};
static_assert(requires { requires TraitTypes<FnTypeGood>; });
static_assert(!requires { requires TraitTypes<FnTypeBad>; });

struct FnGood {
    void foo();
    bool bar() const;
    static int stat();
};
struct FnBad {};
static_assert(requires { requires TraitFns<FnGood>; });
static_assert(!requires { requires TraitFns<FnBad>; });

struct NestedGood : FnTypeGood, FnGood {};
struct NestedBad1 : FnGood {};
struct NestedBad2 : FnTypeGood {};
static_assert(requires { requires TraitNested<NestedGood>; });
static_assert(!requires { requires TraitNested<NestedBad1>; });
static_assert(!requires { requires TraitNested<NestedBad2>; });

Template selection with partially / fully specializations.

enum Kind {
    kPrimary,
    kTT,
    kIntBool,
    kIntInt,
};

// (1) Primary template.
template<typename T, typename U = bool>
struct pair {
    static constexpr Kind kind = kPrimary;
};

// (2) Partial template specialization.
template<typename T>
struct pair<T, T> {
    static constexpr Kind kind = kTT;
};

// (3) Template specialization.
template<>
struct pair<int, bool> {
    static constexpr Kind kind = kIntBool;
};

// (4) Template specialization.
template<>
struct pair<int, int> {
    static constexpr Kind kind = kIntInt;
};

int main() {
    static_assert(pair<int>::kind == kIntBool, "");
    // * Compare template arg list with primary template, we only supplied one
    //   arg, the second one will be defaulted as
    //   pair<int, bool>
    // * Compare template arg list against available specializations, this will
    //   try to match the pattern <int, bool> against the patterns defined in the
    //   partial specializations.
    // * (2) <int, bool> pattern does not match
    // * (3) <int, bool> pattern does match
    // * (4) <int, bool> pattern does not match
    // * Pick the most specialized version -> (3)

    static_assert(pair<char, char>::kind == kTT, "");
    // * Compare template arg list against available specializations, this will
    //   try to match the pattern <char, char> against the patterns defined in the
    //   partial specializations.
    // * (2) <char, char> pattern does match
    // * (3) <char, char> pattern does not match
    // * (4) <char, char> pattern does not match
    // * Pick the most specialized version -> (2)

    static_assert(pair<int, int>::kind == kIntInt, "");
    // * Compare template arg list against available specializations, this will
    //   try to match the pattern <int, int> against the patterns defined in the
    //   partial specializations.
    // * (2) <int, int> pattern does match
    // * (3) <int, int> pattern does match
    // * (4) <int, int> pattern does not match
    // * Pick the most specialized version -> (3)

    static_assert(pair<char, short>::kind == kPrimary, "");
    // * Compare template arg list against available specializations, this will
    //   try to match the pattern <char, short> against the patterns defined in the
    //   partial specializations.
    // * (2) <char, short> pattern does not match
    // * (3) <char, short> pattern does not match
    // * (4) <char, short> pattern does not match
    // * No specialization matches, take the primary template.
}

Example: Perfect forwarding

#include <cassert>
#include <cstdio>
#include <new>
#include <type_traits>
#include <utility>

struct S {};

struct M {
    M() {
        std::puts("M()");
    }
    M(const M&) {
        std::puts("M(M&)");
    }
    M(M&&) {
        std::puts("M(M&&)");
    }
    M& operator=(const M&) = delete;
    M& operator=(M&&) = delete;

    M(S&, int) {
        std::puts("M(S&)");
    }
    M(S&&, int) {
        std::puts("M(S&&)");
    }
    ~M() {
        std::puts("~M()");
    }
};

template<typename T>
struct option {
    static_assert(!std::is_reference_v<T>);

    constexpr option() = default;

    template<typename... Params>
    constexpr option(Params&&... params) : m_has_val(true) {
        // BAD: does not perfectly forward!
        //      eg, if option(S&&) is invoked, this would invoke M(S&).
        // new (&m_val) T(params...);

        // GOOD: perfectly forwards params to constructor of T.
        new (m_val) T(std::forward<Params>(params)...);
    }

    ~option() {
        reset();
    }

    constexpr T& value() {
        assert(m_has_val);
        // Placement new starts a new lifetime, launder pointer returned to the
        // aligned storage.
        //
        // [1] https://en.cppreference.com/w/cpp/utility/launder
        return *__builtin_launder(reinterpret_cast<T*>(m_val));
    }

  private:
    constexpr void reset() {
        if (!m_has_val) {
            return;
        }
        if constexpr (!std::is_trivially_destructible_v<T>) {
            value().~T();
        };
    }

    alignas(T) char m_val[sizeof(T)];
    bool m_has_val{false};
};

int main() {
    std::puts("==> case 1");
    // invokes M(S&&, int)
    option<M> opt1(S{}, 123);

    std::puts("==> case 2");
    // invokes M() + M(M&&)
    option<M> x /* option(M&&) + M(M&&) */ = M{} /* M() */;
}