Template

Instantiate a Template

#include <iostream>

struct A {};
struct B {};

template <typename T, typename U>
struct Foo {
  Foo(T t, U u) : t_(t), u_(u) {}

  T t_;
  U u_;
};

template <typename F, typename T, typename U>
struct Bar {
  Bar(T t, U u) : f_(t, u) {}

  F f_;
};

// instantiate template Foo
template class Foo<A, B>;

int main() {
  Bar<Foo<A, B>, A, B>(A(), B());
  return 0;
}

Template Specialization

#include <iostream>

template <typename T, typename U>
class Base
{
private:
  T m_a;
  U m_b;

public:

  Base(T a, U b) : m_a(a), m_b(b) {};

  T foo() { return m_a; }
  U bar() { return m_b; }
};

// partial specialization
template<typename T>
class Base <T, int>
{
private:
  T m_a;
  int m_b;
public:
  Base(T a, int b) : m_a(a), m_b(b) {}
  T foo() { return m_a; }
  int bar() { return m_b; }
};

// full specialization
template<>
class Base <double, double>
{
private:
  double d_a;
  double d_b;
public:
  Base(double a, double b) : d_a(a), d_b(b) {}
  double foo() { return d_a; }
  double bar() { return d_b; }
};


int main (int argc, char *argv[])
{
  Base<float, int> foo(3.33, 1);
  Base<double, double> bar(55.66, 95.27);
  std::cout << foo.foo() << std::endl;
  std::cout << foo.bar() << std::endl;
  std::cout << bar.foo() << std::endl;
  std::cout << bar.bar() << std::endl;
  return 0;
}

Class Template

#include <iostream>

template <typename T>
class Area
{
protected:
  T w;
  T h;
public:
  Area(T a, T b) : w(a), h(b) {}
  T get() { return w * h; }
};

class Rectangle : public Area<int>
{
public:
  Rectangle(int a, int b) : Area<int>(a, b) {}
};

template <typename T>
class GenericRectangle : public Area<T>
{
public:
  GenericRectangle(T a, T b) : Area<T>(a, b){}
};


int main (int argc, char *argv[])
{
  Rectangle r(2, 5);
  GenericRectangle<double> g1(2.5, 3.);
  GenericRectangle<int> g2(2, 3);

  std::cout << r.get() << std::endl;
  std::cout << g1.get() << std::endl;
  std::cout << g2.get() << std::endl;
  return 0;
}

Variadic Template (Parameter Pack)

#include <iostream>
#include <utility>
#include <vector>

template <typename T>
class Vector {
protected:
  std::vector<T> v;
public:

  template<typename ...Args>
  Vector(Args&&... args) {
    (v.emplace_back(std::forward<Args>(args)), ...);
  }

  using iterator = typename std::vector<T>::iterator;
  iterator begin() noexcept { return v.begin(); }
  iterator end() noexcept { return v.end(); }
};


int main(int argc, char *argv[]) {

  Vector<int> v{1,2,3};
  for (const auto &x : v)
  {
    std::cout << x << "\n";
  }
}

Fold expressions

// g++ -std=c++17 -Wall -Werror -O3 a.cc

#include <iostream>
#include <utility>

template <typename ...Args>
decltype(auto) f(Args&& ...args) {
  auto l = [](auto &&x) { return x * 2; };
  return (l(std::forward<Args>(args)) + ...);
}

int main(int argc, char *argv[]) {
  std::cout << f(1, 2, 3, 4, 5) << std::endl;
}

Limit a Template Types

#include <iostream>
#include <string>
#include <type_traits>

template<typename S,
  typename = typename std::enable_if<
    std::is_same<
      std::string,
      typename std::decay<S>::type
    >::value
  >::type
>
void Foo(S s) {
  std::cout << s << "\n";
}


int main(int argc, char *argv[]) {
  std::string s1 = "Foo";
  const std::string s2 = "Bar";
  Foo(s1);
  Foo(s2);

  // Foo(123);    compile error
  // Foo("Baz");  compile error
}

Specialize Types

#include <iostream>
#include <string>
#include <type_traits>

template<typename S>
void Foo(S s) {
  if (std::is_integral<S>::value) {
    std::cout << "do a task for integer..." << "\n";
    return;
  }
  if (std::is_same<std::string, typename std::decay<s>::type>::value)
  {
    std::cout << "do a task for string..." << "\n";
    return;
  }
}

int main(int argc, char *argv[]) {
  std::string s1 = "Foo";
  Foo(s1);
  Foo(123);
}

Template Specialization approach

#include <iostream>
#include <string>
#include <type_traits>

template<typename S>
void Foo(S s) {}

template <>
void Foo<int>(int s) {
  std::cout << "do a task for integer..." << "\n";
}
template<>
void Foo<std::string>(std::string s) {
  std::cout << "do a task for string..." << "\n";
}


int main(int argc, char *argv[]) {
  std::string s1 = "Foo";
  Foo(s1);
  Foo(123);
}

Curiously recurring template pattern

#include <iostream>

// Curiously Recurring Template Pattern (CRTP)

template <typename D>
class Base
{
public:
  void interface() {
    static_cast<D *>(this)->implement();
  }

  static void static_interface() {
    D::static_interface();
  }

  void implement() {
    std::cout << "Base" << std::endl;
  }
};

class DerivedFoo : public Base<DerivedFoo>
{
public:
  void implement() {
    std::cout << "Foo" << std::endl;
  }
  static void static_interface() {
    std::cout << "Static Foo" << std::endl;
  }
};

class DerivedBar : public Base<DerivedBar> {};

int main (int argc, char *argv[])
{
  DerivedFoo foo;
  DerivedBar bar;

  foo.interface();
  foo.static_interface();
  bar.interface();

  return 0;
}

Parametric Expressions

#include <iostream>

// g++ -std=c++17 -fconcepts -g -O3 a.cpp

decltype(auto) min(auto&& lhs, auto&& rhs) {
  return lhs < rhs ? lhs : rhs;
}

int main(int argc, char *argv[]) {
  std::cout << min(1, 2) << "\n";
  std::cout << min(3.14, 2.718) << "\n";
}
#include <iostream>

template<typename T>
decltype(auto) min(T&& lhs,T&& rhs) {
  return lhs < rhs ? lhs : rhs;
}

int main(int argc, char *argv[]) {
  std::cout << min(1, 2) << "\n";
  std::cout << min(3.14, 2.718) << "\n";
}
#include <iostream>

auto min = [](auto&& lhs, auto&& rhs) {
  return lhs < rhs ? lhs : rhs;
};

int main(int argc, char *argv[]) {
  std::cout << min(1, 2) << "\n";
  std::cout << min(3.14, 2.718) << "\n";
}

Reference

_ Parametric Expressions

Template Template Parameters

#include <vector>
#include <deque>

template <template<class, class> class V, class T, class A>
void f(V<T, A> &v) {
  v.pop_back();
}

int main(int argc, char *argv[]) {
  std::vector<int> v{0};
  std::deque<int> q{1};
  f<std::vector, int>(v);
  f<std::deque, int>(q);
}

Access Protected Membors in Sub-Template

Accessing protected members by pulling the names into the current scope via using.

#include <iostream>

template <typename T>
class A {
 public:
  A(T p) : p_{p} {}
  decltype(auto) f() { std::cout << p_ << "\n"; }
 protected:
  T p_;
};

template <typename T>
class B : A<T> {
  using A<T>::p_;
 public:
  B(T p) : A<T>(p) {}
  decltype(auto) g() { std::cout << p_ << "\n"; }
};

int main(int argc, char *argv[]) {
  A<int> a(0);
  B<int> b(0);
  a.f();
  b.g();
}

Another option is qualifying name via the this pointer.

#include <iostream>

template <typename T>
class A {
 public:
  A(T p) : p_{p} {}
  decltype(auto) f() { std::cout << p_ << "\n"; }
 protected:
  T p_;
};

template <typename T>
class B : A<T> {
 public:
  B(T p) : A<T>{p} {}
  decltype(auto) g() { std::cout << this->p_ << "\n"; }
};

int main(int argc, char *argv[]) {
  A<int> a(0);
  B<int> b(0);
  a.f();
  b.g();
}