---------------
if (is_polymorphic<int>::value) cout << "Wielka niespodzianka!";
---------------
enum class Axis : char { x, y, z };
enum Flags { off, x=1, y=x<<1, z=x<<2, t=x<<3 };

typename std::underlying_type<Axis>::type v1;  // v1 to char
typename std::underlying_type<Flags>::type v2; // v2 to prawdopodobnie int (8.4.2)
---------------
template<typename T, int N>
struct Array_type {
    using type = T;
    static const int dim = N;
    //...
};
---------------
using Array = Array_type<int,3>;

Array::type x;                 // x to int
constexpr int s = Array::dim;  // s to 3
---------------
template<int N>
struct Integer {
    using Error = void;
    using type = Select<N,Error,signed char,short,Error,int,Error,Error,Error,long>;
};

typename Integer<4>::type i4 = 8; // 4-bajtowa liczba cakowita
typename Integer<1>::type i1 = 9; // 1-bajtowa liczba cakowita
---------------
constexpr int on_stack_max = sizeof(std::string);      // maks. rozmiar obiektu, jaki zostanie 
                                                       // alokowany na stosie

template<typename T>
struct Obj_holder {
    using type = typename std::conditional<(sizeof(T)<=on_stack_max),
                                            Scoped<T>, // pierwsza moliwo
                                            On_heap<T> // druga moliwo
                                        >::type;
};
---------------
void f()
{
typename Obj_holder<double>::type v1;            // liczba typu double zostaje na stosie
typename Obj_holder<array<double ,200>>::type v2; // tablica idzie do pamici wolnej
//...
*v1 = 7.7;       // dostp uzyskuje si poprzez wskaniki (v1 zawiera warto typu double)
(*v2)[77] = 9.9; // On_heap zapewnia dostp poprzez wskaniki (v2 zawiera tablic)
}
---------------
template<typename T>
struct On_heap {
    On_heap() :p(new T){}    // alokuje
    ~On_heap() { delete p; } // dezalokuje

    T& operator*() { return *p; }
    T* operator->() { return p; }

    On_heap(const On_heap&) = delete; // uniemoliwia kopiowanie
    On_heap& operator=(const On_heap&) = delete;
private:
    T* p; // wskanik do obiektu w pamici wolnej
};

template<typename T>
struct Scoped {
    Scoped() {}

    T& operator*() { return x; }
    T* operator->() { return &x; }

    Scoped(const Scoped&) = delete; // uniemoliwia kopiowanie
    Scoped& operator=(const Scoped&) = delete;
private:
    Tx; // obiekt
};
---------------
template<typename T>
using Holder = typename Obj_holder<T>::type;

void f2()
{
    Holder<double> v1;
        // double idzie na stos
    Holder<array<double ,200>> v2; // tablica idzie do pamici wolnej
    //...
    *v1 = 7.7;       // dostp uzyskuje si poprzez wskaniki (v1 zawiera warto typu double)
    (*v2)[77] = 9.9; // On_heap zapewnia dostp poprzez wskaniki (v2 zawiera tablic)
}
---------------
template<bool C, typename T, typename F>
using Conditional = typename std::conditional<C,T,F>::type;
---------------
if (p) {
    p->f(7);
    //...
}
---------------
conditional<
    is_integral<T>::value,
    make_unsigned<T>::value,
    Error
    >::type
---------------
typename make_unsigned<T>::type
---------------
Conditional<
    is_integral<T>::value,
    Delay<Make_unsigned,T>,
    Error
>
---------------
template<template<typename...> class F, typename... Args>
using Delay = F<Args...>;
---------------
template<typename T>
void copy(T* p, const T* q, int n)
{
    if (std::is_pod<T>::value)
        memcpy(p,q,n*sizeof(T)); // uycie zoptymalizowanego kopiowania pamici
    else
        for (int i=0; i!=n; ++i)
            p[i] = q[i];         // kopiowanie pojedynczych wartoci
}
---------------
template<typename T>
void copy(T* p, const T* q, int n)
{
    if (is_pod<T>())
    //...
}
---------------
template<typename T>
void do_something()
{
    Conditional<(is_pod<T>()),On_heap<T>,Scoped<T>> x; // bd: is_pod<T>() jest typem
    //...
}
---------------
template<typename T>
constexpr bool Is_pod()
{
    return std::is_pod<T>::value;
}
---------------
template<typename T>
constexpr bool Is_big()
{
    return 100<sizeof(T);
}
---------------
template<typename T>
using Obj_holder = Conditional<(Is_big<T>()), On_heap<T>, Scoped<T>>;
---------------
struct X { // drukuje X
    void operator()(int x) { cout << "X" << x << "!\n"; }
    //...
};

struct Y { // drukuje Y
    void operator()(int y) { cout << "Y" << y << "!\n"; }
    //...
};

void f()
{
    Conditional<(sizeof(int)>4),X,Y>{}(7); // tworzy obiekt typu X lub Y i go wywouje

    using Z = Conditional<(Is_polymorphic<X>()),X,Y>;
    Z zz;  // tworzy X lub Y
    zz(7); // wywouje X lub Y
}
---------------
template<typename Iterator>
struct iterator_traits {
    using difference_type = typename Iterator::difference_type;
    using value_type = typename Iterator::value_type;
    using pointer = typename Iterator::pointer;
    using reference = typename Iterator::reference;
    using iterator_category = typename Iterator::iterator_category;
};
---------------
template<typename Iter>
Iter search(Iter p, Iter q, typename iterator_traits<Iter>::value_type val)
{
    typename iterator_traits<Iter>::difference_type m = q-p;
    //...
}
---------------
template<typename T>
    using Value_type = typename std::iterator_traits<T>::value_type;

template<typename T>
    using Difference_type = typename std::iterator_traits<T>::difference_type;

template<typename T>
    using Iterator_category= typename std::iterator_traits<T>::iterator_category;
---------------
template<typename Iter>
Iter search(Iter p, Iter q, Value_type<Iter> val)
{
    Difference_type<Iter> m = q-p;
    //...
}
---------------
template<typename Iter, typename Val>
Iter search(Iter p, Iter q, Val val)
{
    auto x = *p;  // jeli nie trzeba podawa nazwy typu *p
    auto m = q-p; // jeli nie trzeba podawa nazwy typu q-p

    using value_type = decltype(*p);        // jeli chcemy poda nazw typu *p
    using difference_type = decltype(q-p);  // jeli chcemy poda nazw typu q-p
    //...
}
---------------
template<bool C, typename T, typename F> // oglny szablon
struct conditional {
    using type = T;
};

template<typename T, typename F>         // specjalizacja dla false
struct conditional<false,T,F> {
    using type = F;
};
---------------
typename conditional<(std::is_polymorphic<T>::value),X,Y>::type z;
---------------
template<bool B, typename T, typename F>
using Conditional = typename std::conditional<B,T,F>::type;
---------------
Conditional<(Is_polymorphic<T>()),X,Y> z;
---------------
Conditional<(std::is_polymorphic<T>::value),X,Y> z;
---------------
struct Square {
    constexpr int operator()(int i) { return i*i; }
};

struct Cube {
    constexpr int operator()(int i) { return i*i*i; }
};
---------------
if (My_cond<T>())
    using Type = Square; // bd: deklaracja jako ga instrukcji if
else
    using Type = Cube;   // bd: deklaracja jako ga instrukcji if

Type x;                  // bd: nie ma Type w zakresie
---------------
Conditional<(My_cond<T>()),Square,Cube>{}(99); // wywoanie Square{}(99) lub Cube{}(99)
---------------
((My_cond<T>())?Square:Cube){}(99);
---------------
(My_cond<T>()?Square{}:Cube{})(99); // bd: niezgodne argumenty operatora ?:
---------------
My_cond<T>()?Square{}(99):Cube{}(99);
---------------
Conditional<(My_cond<T>()),Square,Cube>{}(99);
---------------
class Nil {};

template<int I, typename T1 =Nil, typename T2 =Nil, typename T3 =Nil, typename T4 =Nil>
struct select;

template<int I, typename T1 =Nil, typename T2 =Nil, typename T3 =Nil, typename T4 =Nil>
using Select = typename select<I,T1,T2,T3,T4>::type;

// specjalizacje dla 0-3:

template<typename T1, typename T2, typename T3, typename T4>
struct select<0,T1,T2,T3,T4> { using type = T1; }; // specjalizacja dla N==0

template<typename T1, typename T2, typename T3, typename T4>
struct select<1,T1,T2,T3,T4> { using type = T2; }; // specjalizacja dla N==1

template<typename T1, typename T2, typename T3, typename T4>
struct select<2,T1,T2,T3,T4> { using type = T3; }; // specjalizacja dla N==2

template<typename T1, typename T2, typename T3, typename T4>
struct select<3,T1,T2,T3,T4> { using type = T4; }; // specjalizacja dla N==3
---------------
Select<5,int,double,char> x;
---------------
template<int N, typename T1, typename T2, typename T3, typename T4>
Select<N,T1,T2,T3,T4>& get(Tuple<T1,T2,T3,T4>& t); // zobacz 28.5.2

auto x = get<2>(t);  // zaoenie, e t jest Tuple
---------------
template<unsigned N, typename... Cases> // przypadek oglny, nigdy nie konkretyzowany
struct select;

template<unsigned N, typename T, typename... Cases>
struct select<N,T,Cases...> :select<N-1,Cases...> {
};

template<typename T, typename... Cases> // ostateczny przypadek: N==0
struct select<0,T,Cases...> {
    using type = T;
};

template<unsigned N, typename... Cases>
using Select = typename select<N,Cases...>::type;
---------------
template<int N>
constexpr int fac()
{
    return N*fac<N-1>();
}

template<>
constexpr int fac<1>()
{
    return 1;
}
constexpr int x5 = fac<5>();
---------------
constexpr int fac(int i)
{
    return (i<2)?1:i*fac(i-1);
}

constexpr int x6 = fac(6);
---------------
template<int N>
struct Fac {
    static const int value = N*Fac<N-1>::value;
};

template<>
struct Fac<1> {
    static const int value = 1;
};

constexpr int x7 = Fac<7>::value;
---------------
#define IF(c,x,y) typename std::conditional<(c),x,y>::type
---------------
IF(cond,Cube ,Square) z;
---------------
typename std::conditional<(cond),Cube,Square>::type z;
---------------
template<typename T>
class Smart_pointer {
    //...
    T& operator*(); // zwraca referencj do caego obiektu
    T*operator->(); // wybiera skadow (tylko dla klas)
    //...
};
---------------
template<typename T>
class Smart_pointer {
    //...
    T& operator*();                     // zwraca referencj do caego obiektu
    if (Is_class<U>()) U* operator->(); // bd skadni
    //...
};
---------------
template<typename T>
class Smart_pointer {
    //...
    T& operator*();                           // zwraca referencj do caego obiektu
    template<typename U = T>                  // dziwna sztuczka w celu wczenia zasady SFINAE
    Enable_if<Is_class<T>(),T>* operator->(); // wybiera skadow (tylko dla klas)
    //...
};
---------------
template<bool B, typename T =void>
using Enable_if = typename std::enable_if<B,T>::type;

template<typename T> constexpr bool Is_class()
{
    return std::is_class<T>::value;
}
---------------
void f(Smart_pointer<double> p, Smart_pointer<complex<double>> q)
{
    auto d0 =*p;         // OK
    auto c0 =*q;         // OK
    auto d1 = q->real(); // OK
    auto d2 = p->real(); // bd: nie wskazuje obiektu klasy
    //...
}
---------------
Enable_if<Is_class<T>(),T>* operator->();
---------------
declare_if (Is_class<T>()) T* operator->(); // nie C++
---------------
template<typename T>
class vector {
public:
    vector(size_t n, const T& val); // n elementw typu T o wartoci val
    template<typename Iter>
    vector(Iter b, Iter e);         // inicjacja z <b,e)
    //...
};
---------------
vector<int> v(10,20);
---------------
template<typename T>
class vector<T> {
public:
    vector(size_t n, const T& val);  // n elementw typu T o wartoci val

    template<typename Iter, typename =Enable_if<Input_iterator<Iter>()>>
        vector(Iter b, Iter e);      // inicjacja z <b,e)
    //...
};
---------------
template<typename T>
class vector<T> {
public:
    vector(size_t n, const T& val);                  // n elementw typu T o wartoci val

    template<typename Iter>
        vector(Enable_if<Input_iterator<Iter>(),Iter> b, Iter e); // inicjacja z <b,e)
    //...
};
---------------
Enable_if<(version2_2_3<config),My_struct>* make_default() // bd: nie szablon
{
    return new My_struct{};
}

template<typename T>
void f(const T& x)
{
    Enable_if<!(20<sizeof(T)),T> tmp = x;         // bd: tmp nie jest funkcj
    Enable_if<(20<sizeof(T)),T&> tmp = *new T{x}; // bd: tmp nie jest funkcj
    //...
}
---------------
template<bool B, typename T = void>
struct std::enable_if {
    typedef T type;
};

template<typename T>
struct std::enable_if<false, T> {}; // brak ::type, jeli B==false

template<bool B, typename T = void>
using Enable_if = typename std::enable_if<B,T>::type;
---------------
template<typename T>
Enable_if<Ordered<T>()> fct(T*,T*){/* zoptymalizowana implementacja */}

template<typename T>
Enable_if<!Ordered<T>()> fct(T*,T*){/* niezoptymalizowana implementacja */}
---------------
void f(vector<int>& vi, vector<complex<int>>& vc)
{
    if (vi.size()==0 || vc.size()==0) throw runtime_error("niepoprawny argument fct");
    fct(&vi.front(),&vi.back()); // wywoanie wersji zoptymalizowanej
    fct(&vc.front(),&vc.back()); // wywoanie wersji niezoptymalizowanej
}
---------------
struct substitution_failure { }; // reprezentuje porak deklaracji czego
template<typename T>
struct substitution_succeeded : std::true_type
{};

template<>
struct substitution_succeeded<substitution_failure> : std::false_type
{};
---------------
std::true_type::value == true
std::false_type::value == false
---------------
template<typename T>
struct has_f
    : substitution_succeeded<typename get_f_result<T>::type>
{};
---------------
template<typename T>
struct get_f_result {
private:
    template<typename X>
        static auto check(X const& x) -> decltype(f(x));  // mona wywoa f(x)
    static substitution_failure check(...);               // nie mona wywoa f(x)
public:
    using type = decltype(check(std::declval<T>()));
};
---------------
is_valid(f(x)); // czy mona skompilowa f(x)?
---------------
template<typename T>
constexpr bool Has_f()
{
    return has_f<T>::value;
}
---------------
template<typename T>
class X {
    //...
    template<typename U = T>
    Enable_if<Has_f<U>()> use_f(const U& t)
    {
        //...
        f(t);
        //...
    }
    //...
};
---------------
if (Has_f<decltype(t)>()) f(t);
---------------
template<typename Iter, typename Val>
Enable_if<Has_not_equals<Iter>(),Iter> find(Iter first, Iter last, Val v)
{
    while (first!=last && !(*first==v))
        ++first;
    return first;
}

template<typename Iter, typename Val>
Enable_if<!Has_not_equals<Iter>(),Iter> find(Iter first, Iter last, Val v)
{
    while (!(first==last) && !(*first==v))
        ++first;
    return first;
}
---------------
template<typename T>
auto operator!=(const T& a, const T& b) -> decltype(!(a==b))
{
    return !(a==b);
}
---------------
Tuple<double , int, char> x {1.1, 42, 'a'};
cout << x << "\n";
cout << get<1>(x) << "\n";
---------------
{1.1, 42, 'a'}
42
---------------
template<typename T1=Nil, typename T2=Nil, typename T3=Nil, typename T4=Nil>
struct Tuple : Tuple<T2, T3, T4> { // ukad: {T2,T3,T4} przed T1
    T1 x;

    using Base = Tuple<T2, T3, T4>;
    Base* base() { return static_cast<Base*>(this); }
    const Base* base() const { return static_cast<const Base*>(this); }

    Tuple(const T1& t1, const T2& t2, const T3& t3, const T4& t4) :Base{t2,t3,t4}, x{t1} { }
};
---------------
template<>
struct Tuple<> { Tuple() {} }; // 0-tuple

template<typename T1>
struct Tuple<T1> : Tuple<> {   // krotka jednoelementowa
    T1 x;

    using Base = Tuple<>;
    Base* base() { return static_cast<Base*>(this); }
    const Base* base() const { return static_cast<const Base*>(this); }

    Tuple(const T1& t1) :Base{}, x{t1} { }
};

template<typename T1, typename T2>
struct Tuple<T1, T2> : Tuple<T2> { // krotka dwuelementowa, ukad: T2 przed T1
    T1 x;

    using Base = Tuple<T2>;
    Base* base() { return static_cast<Base*>(this); }
    const Base* base() const { return static_cast<const Base*>(this); }

    Tuple(const T1& t1, const T2& t2) :Base{t2}, x{t1} { }
};

template<typename T1, typename T2, typename T3>
struct Tuple<T1, T2, T3> : Tuple<T2, T3> { // krotka trzyelementowa, ukad: {T2,T3} przed T1
    T1 x;

    using Base = Tuple<T2, T3>;
    Base* base() { return static_cast<Base*>(this); }
    const Base* base() const { return static_cast<const Base*>(this); }

    Tuple(const T1& t1, const T2& t2, const T3& t3) :Base{t2, t3}, x{t1} { }
};
---------------
Tuple<double,string,int,char>{3.14,"Bob",127,'c'}
---------------
class FO { /* obiekt funkcyjny bez danych skadowych  */};

typedef Tuple<int*, int*> T0;
typedef Tuple<int*,FO> T1;
typedef Tuple<int*, FO, FO> T2;
---------------
template<typename T1, typename T2, typename T3, typename T4>
void print_elements(ostream& os, const Tuple<T1,T2,T3,T4>& t)
{
    os  << t.x << ", "; // x z t
    print_elements(os,*t.base());
}

template<typename T1, typename T2, typename T3>
void print_elements(ostream& os, const Tuple<T1,T2,T3>& t)
{
    os  << t.x << ", ";
    print_elements(os,*t.base());
}

template<typename T1, typename T2>
void print_elements(ostream& os, const Tuple<T1,T2>& t)
{
    os  << t.x << ", ";
    print_elements(os,*t.base());
}

template<typename T1>
void print_elements(ostream& os, const Tuple<T1>& t)
{
    os << t.x;
}

template<>
void print_elements(ostream& os, const Tuple<>& t)
{
    os << " ";
}
---------------
template<typename T1, typename T2, typename T3, typename T4>
ostream& operator<<(ostream& os, const Tuple<T1,T2,T3,T4>& t)
{
    os << "{ ";
    print_elements(os,t);
    os << " }";
    return os;
}
---------------
Tuple<double, int, char> x {1.1, 42, 'a'};
cout << x << "\n";

cout << Tuple<double,int,int,int>{1.2,3,5,7} << "\n";
cout << Tuple<double,int,int>{1.2,3,5} << "\n";
cout << Tuple<double,int>{1.2,3} << "\n";
cout << Tuple<double>{1.2} << "\n";
cout << Tuple<>{} << "\n";
---------------
{ 1.1, 42, a }
{ 1.2,3,5,7 }
{ 1.2,3,5 }
{ 1.2,3 }
{ 1.2 }
{}
---------------
Tuple<double, int, char> x {1.1, 42, 'a'};

cout << "{ "
    << get<0>(x) << ", "
    << get<1>(x) << ", "
    << get<2>(x) << " }\n"; // drukuje { 1.1, 42, a }

auto xx = get<0>(x);        // xx jest double
---------------
template<int N, typename T1, typename T2, typename T3, typename T4>
Select<N, T1, T2, T3, T4>& get(Tuple<T1, T2, T3, T4>& t)
{
    return getNth<Select<N, T1, T2, T3, T4>,N>::get(t);
}
---------------
template<typename Ret, int N>
struct getNth {           // getNth() zapamituje typ (Ret) N-tego elementu
    template<typename T>
    static Ret& get(T& t) // pobiera element N z bazy t
    {
        return getNth<Ret,N-1>::g et(*t.base());
    }
};

template<typename Ret>
struct getNth<Ret,0> {
    template<typename T>
    static Ret& get(T& t)
    {
        return t.x;
    }
};
---------------
template<typename T>
constexpr auto operator[](T t,int N)
{
    return get<N>(t);
}
---------------
Tuple<double, int, char> x {1.1, 42, 'a'};

get<2>(x) = 'b'; // OK
---------------
const Tuple<double, int, char> xx {1.1, 42, 'a'};

get<2>(xx) = 'b';      // bd: xx jest const
char cc = get<2>(xx);  // bd: xx jest const (niespodzianka?)
---------------
const Tuple<double, int, char> xx {1.1, 422, 'a'};
char cc = get<2>(xx);  // OK: odczyt z const
cout << "xx: " << xx << "\n";
get<2>(xx) = 'x';      // bd: xx jest const
---------------
template<typename Ret, int N>
struct getNth {           // getNth() zapamituje typ (Ret) N-tego elementu
    template<typename T>
    static Ret& get(T& t) // pobiera element N z bazy t
    {
        return getNth<Ret,N-1>::get(*t.base());
    }

    template<typename T>
    static const Ret& get(const T& t) // pobiera element N z bazy t
    {
        return getNth<Ret,N-1>::get(*t.base());
    }
};

template<typename Ret>
struct getNth<Ret,0> {
    template<typename T> static Ret& get(T& t) { return t.x; }
    template<typename T> static const Ret& get(const T& t) { return t.x; }
};

template<int N, typename T1, typename T2, typename T3, typename T4>
Select<N, T1, T2, T3, T4>& get(Tuple<T1, T2, T3, T4>& t)
{
    return getNth<Select<N, T1, T2, T3, T4>,N>::get(t);
}

template<int N, typename T1, typename T2, typename T3, typename T4>
const Select<N, T1, T2, T3, T4>& get(const Tuple<T1, T2, T3, T4>& t)
{
    return getNth<Select<N, T1, T2, T3, T4>,N>::get(t);
}
---------------
template<typename T1, typename T2, typename T3, typename T4>
Tuple<T1, T2, T3, T4> make_tuple(const T1& t1, const T2& t2, const T3& t3, const T4& t4)
{
    return Tuple<T1, T2, T3, T4>{t1, t2, t3,t4};
}

//... pozostae cztery funkcje make_tuple...
---------------
auto xxx = make_tuple(1.2,3,'x',1223);
cout << "xxx: " << xxx << "\n";
---------------
printf("Warto %s wynosi %g\n","x",3.14);

string name = "target";
printf("Warto %s wynosi %P\n",name,Point{34,200});

printf("Warto %s wynosi %g\n",7);
---------------
void printf(const char* s)
{
    if (s==nullptr) return;

    while (*s) {
        if (*s=='%' && *++s!='%') // sprawdzenie, czy nie jest oczekiwanych wicej argumentw
                                  // %% reprezentuje znak % w acuchu formatu
            throw runtime_error("Niepoprawny format: brak argumentw");
        std::cout << *s++;
    }
}
---------------
template<typename T, typename... Args> // lista argumentw szablonu zmiennego: jeden lub wicej argumentw
void printf(const char* s, T value , Args... args)  // lista argumentw funkcji: dwa argumenty lub wicej
{
    while (s && *s) {
        if (*s=='%' && *++s!='%') {       // specyfikator formatu (niewane ktry)
            std::cout << value;           // uyj pierwszego nieformatujcego argumentu
            return printf(++s, args...);  // wywoanie rekurencyjne z ogonem listy argumentw
        }
        std::cout << *s++;
    }
    throw std::runtime_error("Dostarczono dodatkowe argumenty do funkcji printf");
}
---------------
void printf(const char* s, T value , Args... args);
---------------
void printf(const char*);
---------------
template<typename T, typename... Args> // lista argumentw szablonu zmiennego: jeden lub wicej argumentw
void printf(const char* s, T value, Args... args)  // lista argumentw funkcji: dwa lub wicej argumentw
{
    while (s && *s) {
        if (*s=='%') { // specyfikator formatu czy %%
            switch (*++s) {
            case '%': // nie specyfikator formatu
                break;
            case 's':
                if (!Is_C_style_string<T>() && !Is_string<T>())
                    throw runtime_error("Niepoprawny format printf()");
                break;
            case 'd':
                if (!Is_integral<T>()) throw runtime_error("Niepoprawny format printf()");
                break;
            case 'g':
                if (!Is_floating_point<T>()) throw runtime_error("Niepoprawny format 
                (printf()");
                break;
            }
            std::cout << value;          // uyj pierwszego nie formatujcego argumentu
            return printf(++s, args...); // wywoanie rekurencyjne z ogonem listy argumentw
        }
        std::cout << *s++;
    }
    throw std::runtime_error("Dostarczono dodatkowe argumenty do funkcji printf");
}
---------------
template<typename... Types>
void f(Types... args);  // zmienny szablon funkcji
---------------
f();                // OK: args nie zawiera adnych argumentw
f(1);               // OK: args zawiera jeden argument - int
f(2, 1.0);          // OK: args zawiera dwa argumentu - int i double
f(2, 1.0, "Witaj"); // OK: args zawiera trzy argumenty -int, double oraz const char*
---------------
template<typename... Types>
void f(Types... args);  // zmienny szablon funkcji
---------------
template<typename T, typename... Args>
void printf(const char* s, T value, Args... args)
{
    //...
    return printf(++s, args...);  // wykonuje rekurencyjne wywoanie z elementami args jako argumentami
    //...
}
---------------
template<typename... Bases>
class X : public Bases... {
public:
    X(const Bases&... b) : Bases(b)... { }
};

X<> x0;
X<Bx> x1(1);
X<Bx,By> x2(2,3);
X<Bx,By,Bz> x3(2,3,4);
---------------
template<typename... Types>
class tuple {
    //...
    template<typename T, typename U, typename = Enable_if<sizeof...(Types)==2>
        tuple(const pair<T,U>&);
};
---------------
template<typename F, typename... T>
void call(F&& f, T&&... t)
{
    f(forward<T>(t)...);
}
---------------
void g0()
{
    cout << "g0()\n";
}

template<typename T>
void g1(const T& t)
{
    cout << "g1(): " << t << '\n';
}

void g1d(double t)
{
    cout << "g1d(): " << t << '\n';
}

template<typename T, typename T2>
void g2(const T& t, T2&& t2)
{
    cout << "g2(): " << t << ' ' << t2 << '\n';
}

void test()
{
    call(g0);
    call(g1); // bd: za mao argumentw
    call(g1<int>,1);
    call(g1<const char*>,"witaj,");
    call(g1<double>,1.2);
    call(g1d,1.2);
    call(g1d,"Nigdy w yciu!");       // bd: niepoprawny typ argumentu dla g1d()
    call(g1d,1.2,"Nie umiem liczy"); // bd: za duo argumentw dla g1d()
    call(g2<double ,string>,1,"wiecie!");

    int i = 99; // testowanie przy uyciu wartoci lewostronnych
    const char* p = "Prba";
    call(g2<double,string>,i,p);

    call([](){ cout <<"l1()\n"; });
    call([](int i){ cout <<"l0(): " << i << "\n";},17);
    call([i](){ cout <<"l1(): " << i << "\n"; });
}
---------------
template<typename... Values> class tuple; // szablon podstawowy

template<> class tuple<> { }; // specjalizacja dla zera elementw

template<typename Head, typename... Tail>
class tuple<Head, Tail...>
    : private tuple<Tail...> { // tu jest rekurencja
/*
    Zasadniczo krotka zapisuje swoj gow (pierwsz par (typ,warto))
    i derywuje z krotki swojego ogona (pozostaych par (typ-warto)).
    Zwr uwag, e typ jest zakodowany w typie, nie za zapisany jako dane
*/
    typedef tuple<Tail...> inherited;
public:
    constexpr tuple() { } // domylny: pusta krotka

    tuple(Add_const_reference<Head> h, Add_const_reference<Tail>... t) // z osobnych argumentw
        : m_head(h), inherited(t...) { }

    template<typename... VValues> // z innej krotki
    tuple(const tuple<VValues...>& other)
        : m_head(other.head()), inherited(other.tail()) { }
    template<typename... VValues>
    tuple& operator=(const tuple<VValues...>& other) // przypisanie
    {
        m_head = other.head();
        tail() = other.tail();
        return *this;
    }
    //...

protected:
    Head m_head;
private:
    Add_reference<Head> head() { return m_head; }
    Add_const_reference<const Head> head() const { return m_head; }

    inherited& tail() { return *this; }
    const inherited& tail() const { return *this; }
};
---------------
template<typename Head, typename... Tail>
Head& head(tuple<Head,Tail...>& t)
{
    return std::get<0>(t);  // pobiera pierwszy element t (34.2.4.2)
}

template<typename Head, typename... Tail>
tuple<Tail&...> tail(tuple<Head, Tail...>& t)
{
    return /* szczegy */;
}
---------------
tuple<string,vector<int>,double> tt("witaj",{1,2,3,4},1.2);
string h = head(tt); // "witaj"
tuple<vector<int>,double> t2 = tail(tt); // {{1,2,3,4},1.2};
---------------
template<typename... Types>
tuple<Types...> make_tuple(Types&&... t) // uproszczenie (iso.20.4.2.4)
{
    return tuple<Types...>(t...);
}

string s = "witaj";
vector<int> v = {1,22,3,4,5};
auto x = make_tuple(s,v,1.2);
---------------
auto t = make_tuple("Witaj, krotko", 43, 3.15);
double d = get<2>(t);  // d ma warto 3.15
---------------
template<size_t N> // drukuje element N i nastpne elementy
struct print_tuple {
    template<typename... T>
    static typename enable_if<(N<sizeof...(T))>::type
    print(ostream& os, const tuple<T...>& t) // niepusta krotka
    {
        os << ", " << get<N>(t);             // drukuje element
        print_tuple<N+1>::print(os,t);       // drukuje pozostae elementy
    }

    template<typename... T>
    static typename enable_if<!(N<sizeof...(T))>::type // pusta krotka
    print(ostream&, const tuple<T...>&)
    {
    }
};
---------------
std::ostream& operator << (ostream& os, const tuple<>&)    // pusta krotka
{
    return os << "{}";
}

template<typename T0, typename ...T>
ostream& operator<<(ostream& os, const tuple<T0, T...>& t) // niepusta krotka
{
    os << '{' << std::get<0>(t);  // drukuje pierwszy element
    print_tuple<1>::print(os,t);  // drukuje pozostae elementy
    return os << '}';
}
---------------
void user()
{
    cout << make_tuple() << '\n';
    cout << make_tuple("Jeden klopsik!") << '\n';
    cout << make_tuple(1,1.2,"Ogon!") << '\n';
}
---------------
auto distance = 10_m;       // 10 metrw
auto time = 20_s;           // 20 sekund
auto speed = distance/time; // 0,5 m/s (metrw na sekund)

if (speed == 20)            // bd: 20 nie ma wymiaru
//...
if (speed == distance)      // bd: nie mona porwnywa m z m/s
//...
if (speed == 10_m/20_s)     // OK: jednostki pasuj
//...
Quantity<MpS2> acceleration = distance/square(time); // MpS2 oznacza m/(s*s)

cout << "speed==" << speed << " acceleration==" << acceleration << "\n";
---------------
template<int M, int K, int S>
struct Unit {
    enum { m=M, kg=K, s=S };
};
---------------
using M = Unit<1,0,0>;     // metry
using Kg = Unit<0,1,0>;    // kilogramy
using S = Unit<0,0,1>;     // sekundy
using MpS = Unit<1,0,-1>;  // metry na sekund (m/s)
using MpS2 = Unit<1,0,-2>; // metry na sekund do kwadratu (m/(s*s))
---------------
template<typename U1, typename U2>
struct Uplus {
    using type = Unit<U1::m+U2::m, U1::kg+U2::kg, U1::s+U2::s>;
};

template<typename U1, typename U2>
using Unit_plus = typename Uplus<U1,U2>::type;
---------------
template<typename U1, typename U2>
struct Uminus {
    using type = Unit<U1::m-U2::m, U1::kg-U2::kg, U1::s-U2::s>;
};

template<typename U1, U2>
using Unit_minus = typename Uminus<U1,U2>::type;
---------------
template<typename U>
struct Quantity {
    double val;
    explicit constexpr Quantity(double d) : val{d} {}
};
---------------
Quantity<M> x {10.5}; // x wynosi 10,5 metra
Quantity<S> y {2};    // y wynosi 2 sekundy
---------------
Quantity<MpS> s = 7; // bd: prba konwersji int na metry na sekund
Quantity<M> comp(Quantity<M>);
//...
Quantity<M> n = comp(7); // bd: comp() wymaga podania odlegoci
---------------
template<typename U>
Quantity<U> operator+(Quantity<U> x, Quantity<U> y) // ten sam wymiar
{
    return Quantity<U>{x.val+y.val};
}

template<typename U>
Quantity<U> operator-(Quantity<U> x, Quantity<U> y) // ten sam wymiar
{
    return Quantity<U>{x.val-y.val};
}
---------------
template<typename U1, typename U2>
Quantity<Unit_plus<U1,U2>> operator*(Quantity<U1> x, Quantity<U2> y)
{
    return Quantity<Unit_plus<U1,U2>>{x.val*y.val};
}

template<typename U1, typename U2>
Quantity<Unit_minus<U1,U2>> operator/(Quantity<U1> x, Quantity<U2> y)
{
    return Quantity<Unit_minus<U1,U2>>{x.val/y.val};
}
---------------
Quantity<MpS> speed {10};
auto double_speed = Quantity<Unit<0,0,0>>{2}*speed;
---------------
template<typename U>
Quantity<U> operator*(Quantity<U> x, double y)
{
    return Quantity<U>{x.val*y};
}

template<typename U>
Quantity<U> operator*(double x, Quantity<U> y)
{
    return Quantity<U>{x*y.val};
}
---------------
Quantity<MpS> speed {10};
auto double_speed = 2*speed;
---------------
Quantity<MpS> speed {10};
auto increased_speed = 2.3+speed; // bd: nie mona doda bezwymiarowego skalara do prdkoci
---------------
auto distance = Quantity<M>{10}; // 10 metrw
auto time = Quantity<S>{20};     // 20 sekund
auto speed = distance/time;      // 0,5 m/s (metrw na sekund)
---------------
auto distance = 10.0;       // 10 metrw
double time = 20;           // 20 sekund
auto speed = distance/time; // 0,5 m/s (metrw na sekund)
---------------
constexpr Quantity<M> operator"" _m(long double d) { return Quantity<M>{d}; }
constexpr Quantity<Kg> operator"" _kg(long double d) { return Quantity<Kg>{d}; }
constexpr Quantity<S> operator"" _s(long double d) { return Quantity<S>{d}; }
---------------
auto distance = 10_m;       // 10 metrw
auto time = 20_s;           // 20 sekund
auto speed = distance/time; // 0,5 m/s (metrw na sekund)

if (speed == 20)            // bd: 20 nie ma wymiaru
//...
if (speed == distance)      // bd: nie mona porwnywa m z m/s
//...
if (speed == 10_m/20_s)     // OK: jednostki pasuj
---------------
constexpr Quantity<M> operator"" _km(long double d) { return 1000*d; }
constexpr Quantity<Kg> operator"" _g(long double d) { return d/1000; }
constexpr Quantity<Kg> operator"" _mg(long double d) { return d/10000000; }  // miligram
constexpr Quantity<S> operator"" _ms(long double d) { return d/1000; }       // milisekundy
constexpr Quantity<S> operator"" _us(long double d) { return d/1000; }       // mikrosekundy
constexpr Quantity<S> operator"" _ns(long double d) { return d/1000000000; } // nanosekundy
//...
---------------
template<typename U>
constexpr Quantity<Unit_plus<U,U>> square(Quantity<U> x)
{
    return Quantity<Unit_plus<U,U>>(x.val*x.val);
}
---------------
template<typename U>
bool operator==(Quantity<U> x, Quantity<U> y)
{
    return x.val==y.val;
}

template<typename U>
bool operator!=(Quantity<U> x, Quantity<U> y)
{
    return x.val!=y.val;
}
---------------
string suffix(int u, const char* x) // funkcja pomocnicza
{
    string suf;
    if (u) {
        suf += x;
        if (1<u) suf += '0'+u;

        if (u<0) {
            suf += '-';
            suf += '0'-u;
        }
    }
    return suf;
}
template<typename U>
ostream& operator<<(ostream& os, Quantity<U> v)
{
    return os << v.val << suffix(U::m,"m") << suffix(U::kg,"kg") << suffix(U::s,"s");
}
---------------
auto distance = 10_m;       // 10 metrw
auto time = 20_s;           // 20 sekund
auto speed = distance/time; // 0,5 m/s (metrw na sekund)

if (speed == 20)            // bd: 20 nie ma wymiaru
//...
if (speed == distance)      // bd: nie mona porwnywa m z m/s
//...
if (speed == 10_m/20_s)     // OK: jednostki pasuj
//...

Quantity<MpS2> acceleration = distance/square(time); // MpS2 oznacza m/(s*s)

cout << "speed==" << speed << " acceleration==" << acceleration << "\n";