c++

Type deduction

Force compile error to see what auto is deduced to.

auto foo = bar();

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

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');
}

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;
}