C++ Templates

【C++ Templates(5)】Tricky Basic

2018-04-11  本文已影响21人  downdemo

关键字typename

// basics/printcoll.hpp
#include <iostream>

// print elements of an STL container
template<typename T>
void printcoll (T const& coll)
{
    typename T::const_iterator pos;  // iterator to iterate over coll
    typename T::const_iterator end(coll.end());  // end position
    for (pos=coll.begin(); pos!=end; ++pos) {
        std::cout << *pos << ' ';
    }
    std::cout << '\n';
}
class stlcontainer {
 public:
    using iterator = ...; // iterator for read/write access
    using const_iterator = ...; // iterator for read access
    ...
};
typename T::const_iterator pos;

零初始化

template <typename T>
void foo()
{
    T x; // T为内置类型则不会初始化  
}
template <typename T>
void foo()
{
    T x{}; // T为内置类型则x为0(或false)
    // C++11前的语法写为
    // T x = T();
}
template <typename T>
class A {
private:
    T x;
public:
    A() : x() {} // 确保x已被初始化,即使是内置类型
    ...
};
template <typename T>
class A {
private:
    T x{};
    ...
};
template<typename T>
void foo(T p{}) { // ERROR
    ...
}
template<typename T>
void foo(T p = T{}) { // OK(must use T() before C++11)
    ...
}

使用this->

template <typename T>
class B {
public:
    void f();
};

template <typename T>
class D : B<T> {
public:
    void f2() { f(); } // 会调用外部的f或者出错
};

原始数组与字符串字面值(string literal)模板

template <typename T>
T const& max(T const& a, T const& b)
{
    return a < b ? b : a;
}

max("apple", "peach"); // OK
max("apple", "banana"); // 错误:类型不同,分别是char const[6]和char const[7]
// basics/lessarray.hpp

template<typename T,  int N, int M>
bool less (T(&a)[N], T(&b)[M])
{
    for (int i = 0; i<N && i<M; ++i) {
        if (a[i]<b[i]) return true;
        if (b[i]<a[i]) return false;
    }
    return N < M;
}

int x[] = {1, 2, 3};
int y[] = {1, 2, 3, 4, 5};
std::cout << less(x,y) << '\n'; // T=int, N=3,M=5
std::cout << less("ab","abc") << '\n'; // T=char const, N=3,M=4
// basics/lessstring.hpp

template<int N, int M>
bool less (char const(&a)[N], char const(&b)[M])
{
    for (int i = 0; i<N && i<M; ++i) {
        if (a[i]<b[i]) return true;
        if (b[i]<a[i]) return false;
    }
    return N < M;
}
// basics/arrays.hpp

#include <iostream>

template<typename T>
struct MyClass; // primary template

template<typename T, std::size_t SZ>
struct MyClass<T[SZ]> // partial specialization for arrays of known bounds
{
    static void print() { std::cout << "print() for T[" << SZ << "]\n"; }
}; 

template<typename T, std::size_t SZ>
struct MyClass<T(&)[SZ]> // partial spec. for references to arrays of known bounds
{
    static void print() { std::cout << "print() for T(&)[" << SZ << "]\n"; }
}; 

template<typename T>
struct MyClass<T[]> // partial specialization for arrays of unknown bounds
{
    static void print() { std::cout << "print() for T[]\n"; }
}; 

template<typename T>
struct MyClass<T(&)[]> // partial spec. for references to arrays of unknown bounds
{
    static void print() { std::cout << "print() for T(&)[]\n"; }
}; 

template<typename T>
struct MyClass<T*> // partial specialization for pointers
{
    static void print() { std::cout << "print() for T*\n"; }
};


// basics/arrays.cpp

#include "arrays.hpp"

template<typename T1, typename T2, typename T3>
void foo(int a1[7], int a2[],    // pointers by language rules
         int (&a3)[42],          // reference to array of known bound
         int (&x0)[],            // reference to array of unknown bound
         T1 x1,                  // passing by value decays
         T2& x2, T3&& x3)        // passing by reference
{
    MyClass<decltype(a1)>::print();      // uses MyClass<T*>
    MyClass<decltype(a2)>::print();      // uses MyClass<T*>
    MyClass<decltype(a3)>::print();      // uses MyClass<T(&)[SZ]>
    MyClass<decltype(x0)>::print();      // uses MyClass<T(&)[]>
    MyClass<decltype(x1)>::print();      // uses MyClass<T*>
    MyClass<decltype(x2)>::print();      // uses MyClass<T(&)[]>
    MyClass<decltype(x3)>::print();      // uses MyClass<T(&)[]>
} 

int main()
{
    int a[42];
    MyClass<decltype(a)>::print();       // uses MyClass<T[SZ]> 
    extern int x[];                      // forward declare array
    MyClass<decltype(x)>::print();       // uses MyClass<T[]> 
    foo(a, a, a, x, x, x, x);
} 

int x[] = {0, 8, 15};                // define forward-declared array

成员模板

Stack<int> intStack1, intStack2;  // stacks for ints
Stack<float> floatStack;          // stack for floats
...
intStack1 = intStack2;            // OK: stacks have same type
floatStack = intStack1;           // ERROR: stacks have different types
// basics/stack5decl.hpp

template<typename T>

class Stack {
private:
    std::deque<T> elems;        // elements

public:
    void push(T const&);        // push element
    void pop();                 // pop element
    T const& top() const;       // return top element
    bool empty() const {        // return whether the stack is empty
        return elems.empty();
    } 

    // assign stack of elements of type T2
    template<typename T2>
    Stack& operator= (Stack<T2> const&);
};
// basics/stack5assign.hpp

template<typename T>
    template<typename T2>
Stack<T>& Stack<T>::operator= (Stack<T2> const& op2)
{
    Stack<T2> tmp(op2);              // create a copy of the assigned stack
    elems.clear();                   // remove existing elements
    while (!tmp.empty()) {           // copy all elements
        elems.push_front(tmp.top());
        tmp.pop();
    }
    return *this;
}
// basics/stack6decl.hpp

template<typename T>
class Stack {
private:
    std::deque<T> elems;
public:
    void push(T const&);
    void pop();
T const& top() const;
    bool empty() const {
        return elems.empty();
    }

    // assign stack of elements of type T2
    template<typename T2>
    Stack& operator= (Stack<T2> const&);

    // to get access to private members of Stack<T2> for any type T2
    template<typename> friend class Stack;
};
// basics/stack6assign.hpp

template<typename T>
    template<typename T2>
Stack<T>& Stack<T>::operator= (Stack<T2> const& op2)
{
    elems.clear();                        // remove existing elements
    elems.insert(elems.begin(),           // insert at the beginning
                 op2.elems.begin(),       // all elements from op2
                 op2.elems.end());
    return *this;
}
Stack<int> intStack;      // stack for ints
Stack<float> floatStack;  // stack for floats
...
floatStack = intStack;    // OK: stacks have different types,
                          //     but int converts to float
elems.push_front(tmp.top());
Stack<std::string> stringStack; // stack of strings
Stack<float> floatStack; // stack of floats
...
floatStack = stringStack; // ERROR: std::string doesn't convert to float
// basics/stack7decl.hpp

template<typename T, typename Cont = std::deque<T>>
class Stack {
private:
    Cont elems;                // elements
public:
    void push(T const&);       // push element
    void pop();                // pop element
    T const& top() const;      // return top element
    bool empty() const {       // return whether the stack is empty
        return elems.empty();
    } 
    // assign stack of elements of type T2
    template<typename T2, typename Cont2>
    Stack& operator= (Stack<T2,Cont2> const&);

    // to get access to private members of Stack<T2> for any type T2:
    template<typename, typename> friend class Stack;
};
// basics/stack7assign.hpp

template<typename T, typename Cont>
    template<typename T2, typename Cont2>
Stack<T,Cont>&
Stack<T,Cont>::operator= (Stack<T2,Cont2> const& op2)
{
    elems.clear(); // remove existing elements
    elems.insert(elems.begin(),           // insert at the beginning
                 op2.elems.begin(),       // all elements from*  op2
                 op2.elems.end());
    return  *this;
}
Stack<int,std::vector<int>> vStack;
...
vStack.push(42); vStack.push(7);
std::cout << vStack.top() << '\n';
// basics/boolstring.hpp

class BoolString {
private:
    std::string value;
public:
    BoolString (std::string const& s)
     : value(s) {
    }

    template<typename T = std::string>
    T get() const {         // get value (converted to T)
        return value;
    }
};
// basics/boolstringgetbool.hpp

// full specialization for BoolString::getValue<>() for bool
template<>
inline bool BoolString::get<bool>() const {
    return value == "true" || value == "1" || value == "on";
}
std::cout << std::boolalpha;
BoolString s1("hello");
std::cout << s1.get() << '\n';        //prints hello
std::cout << s1.get<bool>() << '\n';  //prints false
BoolString s2("on");
std::cout << s2.get<bool>() << '\n';  //prints true
template<unsigned long N>
void printBitset (std::bitset<N> const& bs) {
    std::cout << bs.template to_string<char, std::char_traits<char>,
                                     std::allocator<char>>();
}
[] (auto x, auto y) {
  return x + y;
}
// 等价于下面这个类的一个默认构造对象的简写
class SomeCompilerSpecificName {
public:
    SomeCompilerSpecificName();  // constructor only callable by compiler
    template<typename T1, typename T2>
    auto operator() (T1 x, T2 y) const {
      return x + y;
    }
};

变量模板(Variable Template)

template<typename T>
constexpr T pi{3.1415926535897932385};
std::cout << pi<double> << '\n';
std::cout << pi<float> << '\n';
//== header.hpp:
template<typename T> T val{};     // zero initialized value

//== translation unit 1:
#include "header.hpp"

int main()
{
    val<long> = 42;
    print();
} 

//== translation unit 2:
#include "header.hpp"

void print()
{
    std::cout << val<long> << '\n'; // OK: prints 42
}
template<typename T = long double>
constexpr T pi = T{3.1415926535897932385};

std::cout << pi<> << '\n';       //outputs a long double
std::cout << pi<float> << '\n';  //outputs a float
std::cout << pi << '\n';        //ERROR
#include <iostream>
#include <array> 

template<int N>
    std::array<int,N> arr{}; // array with N elements, zero-initialized
template<auto N>
    constexpr decltype(N) dval = N;  // type of dval depends on passed value

int main()
{
    std::cout << dval<'c'> << '\n'; //N has value 'c' of type char
    arr<10>[0] = 42; // sets first element of global arr
    for (std::size_t i=0; i<arr<10>.size(); ++i) { // uses values set in arr
        std::cout << arr<10>[i] << '\n';
    }
}
template<typename T>
class MyClass {
public:
    static constexpr int max = 1000;
};

template<typename T>
int myMax = MyClass<T>::max;
auto i = myMax<std::string>;
// instead of
auto i = MyClass<std::string>::max;
namespace std {
    template<typename T> class numeric_limits {
    public:
        ...
        static constexpr bool is_signed = false;
        ...
    };
}

template<typename T>
constexpr bool isSigned = std::numeric_limits<T>::is_signed;

isSigned<char>
// instead of
std::numeric_limits<char>::is_signed
std::is_const_v<T>        // since C++17
// instead of
std::is_const<T>::value        //since C++11
// the standard library defines
namespace std {
    template<typename T> constexpr bool is_const_v = is_const<T>::value;
}

模板的模板参数

Stack<int, std::vector<int>> vStack;
// 通过模板的模板参数可以写为
Stack<int, std::vector> vStack;
// basics/stack8decl.hpp
template<typename T,
    template<typename Elem> class Cont = std::deque>
class Stack {
private:
    Cont<T> elems;             // elements

public:
    void push(T const&);       // push element
    void pop();                // pop element
    T const& top() const;      // return top element
    bool empty() const {       // return whether the stack is empty
        return elems.empty();
    }
    ...
};
template<typename T,
    template<class Elem> class Cont = std::deque>
class Stack {                                 //OK
    ...
};

// Since C++17
template<typename T,
    template<typename Elem> typename Cont = std::deque>
class Stack {                                // ERROR before C++17
    ...
};
template<typename T,
    template<typename Elem,
        typename Alloc = std::allocator<Elem>>
    class Cont = std::deque>
class Stack {
private:
    Cont<T> elems;
    ...
};
// basics/stack9.hpp

#include <deque>
#include <cassert>
#include <memory>

template<typename T,
    template<typename Elem,
        typename = std::allocator<Elem>>
    class Cont = std::deque>
class Stack {
private:
    Cont<T> elems;

public:
    void push(T const&);
    void pop();
    T const& top() const;
    bool empty() const {
        return elems.empty();
    }

    // assign stack of elements of type T2
    template<typename T2,
             template<typename Elem2,
                      typename = std::allocator<Elem2>
                     >class Cont2>
    Stack<T,Cont>& operator= (Stack<T2,Cont2> const&);

    // to get access to private members of any Stack with elements of type T2
    template<typename, template<typename, typename>class>
    friend class Stack;
};

template<typename T, template<typename,typename> class Cont>
void Stack<T,Cont>::push (T const& elem)
{
    elems.push_back(elem);
}

template<typename T, template<typename,typename> class Cont>
void Stack<T,Cont>::pop ()
{
    assert(!elems.empty());
    elems.pop_back();
}

template<typename T, template<typename,typename> class Cont>
T const& Stack<T,Cont>::top () const
{
    assert(!elems.empty());
    return elems.back();
}

template<typename T, template<typename,typename> class Cont>
    template<typename T2, template<typename,typename> class Cont2>
Stack<T,Cont>&
Stack<T,Cont>::operator= (Stack<T2,Cont2> const& op2)
{
    elems.clear();                        // remove existing elements
    elems.insert(elems.begin(),           // insert at the beginning
                 op2.elems.begin(),       // all elements from op2
                 op2.elems.end());
    return  *this;
}
// basics/stack9test.cpp

#include  "stack9.hpp"
#include <iostream>
#include <vector>

int main()
{
    Stack<int>   iStack;
    Stack<float> fStack;

    iStack.push(1);
    iStack.push(2);
    std::cout << "iStack.top(): " << iStack.top() << '\n';

    fStack.push(3.3);
    std::cout << "fStack.top(): " << fStack.top() << '\n';

    // assign stack of different type and manipulate again
    fStack = iStack;
    fStack.push(4.4);
    std::cout << "fStack.top(): " << fStack.top() << '\n';

    // stack for doubles using a vector as an internal container
    Stack<double, std::vector> vStack;
    vStack.push(5.5);
    vStack.push(6.6);
    std::cout << "vStack.top(): " << vStack.top() << '\n';

    vStack = fStack;
    std::cout << "vStack: ";
    while (! vStack.empty()) {
        std::cout << vStack.top() << ' ';
        vStack.pop();
    }
    std::cout << '\n';
}
iStack.top(): 2
fStack.top(): 3.3
fStack.top(): 4.4
vStack.top(): 6.6
vStack: 4.4 2 1
上一篇 下一篇

猜你喜欢

热点阅读