mirror of
https://github.com/wuye9036/CppTemplateTutorial.git
synced 2024-03-22 13:11:16 +08:00
e02f4f5c0a
更新了示例代码。
912 lines
15 KiB
C++
912 lines
15 KiB
C++
#include "stdafx.h"
|
||
#include <vector>
|
||
#include <cstdint>
|
||
|
||
#define WRONG_CODE_ENABLED 0
|
||
|
||
// 0. Basic Form
|
||
namespace _0
|
||
{
|
||
template <typename T> // Old fasion: template <class T>
|
||
class ClassA
|
||
{
|
||
T a;
|
||
T* b;
|
||
T foo();
|
||
void foo2(T const&);
|
||
};
|
||
|
||
template <int Sz>
|
||
class ClassB
|
||
{
|
||
int arr[Sz];
|
||
};
|
||
|
||
size_t a = sizeof(ClassB<3>);
|
||
size_t b = sizeof(ClassB<7>);
|
||
|
||
template <typename T> void FunctionA(T const& param)
|
||
{
|
||
}
|
||
|
||
template <typename T> T FunctionB()
|
||
{
|
||
return T();
|
||
}
|
||
}
|
||
|
||
// 1.1 Nested in Class
|
||
namespace _1_1
|
||
{
|
||
template <typename T> // Old fasion: template <class T>
|
||
class ClassA
|
||
{
|
||
T a;
|
||
T* b;
|
||
T foo();
|
||
template <typename U> void foo2(T const&, U const&);
|
||
};
|
||
}
|
||
|
||
// 1.2 Instanciating 1
|
||
namespace _1_2
|
||
{
|
||
_1_1::ClassA<int> a;
|
||
|
||
#if WRONG_CODE_ENABLED
|
||
_1_1::ClassA<WhatTheFuck> b; // Wrong
|
||
_1_1::ClassA c; // Wrong
|
||
#endif
|
||
}
|
||
|
||
// 1.2.2
|
||
|
||
namespace _1_2_2
|
||
{
|
||
template <typename T> T Add(T a, T b)
|
||
{
|
||
return a + b;
|
||
}
|
||
|
||
template <typename SrcT, typename DstT> DstT c_style_cast(SrcT v)
|
||
{
|
||
return (DstT)(v);
|
||
}
|
||
|
||
#if WRONG_CODE_ENABLED
|
||
void foo()
|
||
{
|
||
int a = 0;
|
||
int b = 0;
|
||
char c = 0;
|
||
Add(b, c);
|
||
}
|
||
|
||
void foo2()
|
||
{
|
||
int v = 0;
|
||
float i = c_style_cast<float>(v);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
// 1.3 Instanciating 2
|
||
namespace _1_3
|
||
{
|
||
template <int i> class A
|
||
{
|
||
public:
|
||
void foo()
|
||
{
|
||
}
|
||
};
|
||
template <uint8_t a, typename b, void* c> class B {};
|
||
template <void (*a)()> class C {};
|
||
template <void (A<3>::*a)()> class D {};
|
||
|
||
#if WRONG_CODE_ENABLED
|
||
template <float a> class E {};
|
||
#endif
|
||
|
||
void foo()
|
||
{
|
||
A<5> a;
|
||
B<7, A<5>, nullptr> b;
|
||
C<&foo> c;
|
||
D<&A<3>::foo> d;
|
||
#if WRONG_CODE_ENABLED
|
||
int x = 3;
|
||
A<x> b;
|
||
#endif
|
||
}
|
||
|
||
#if WRONG_CODE_ENABLED
|
||
const char* s = "abc";
|
||
template <char const* s> class S
|
||
{
|
||
};
|
||
|
||
void foo2()
|
||
{
|
||
S<"abc"> i;
|
||
}
|
||
#endif
|
||
|
||
template <typename T>
|
||
class ClassB
|
||
{
|
||
T* a;
|
||
};
|
||
|
||
template <typename T>
|
||
class ClassC
|
||
{
|
||
T a;
|
||
};
|
||
|
||
struct StructA; // Declared but not be defined
|
||
ClassB<StructA> d; // Right
|
||
#if WRONG_CODE_ENABLED
|
||
ClassC<StructA> e; // Wrong
|
||
#endif
|
||
}
|
||
|
||
namespace _2_2_2
|
||
{
|
||
template <typename T> class AddFloatOrMulInt
|
||
{
|
||
static T Do(T a, T b)
|
||
{
|
||
// 在这个例子里面一般形式里面是什么内容不重要,因为用不上
|
||
// 这里就随便给个0吧。
|
||
return T(0);
|
||
}
|
||
};
|
||
|
||
// 其次,我们要指定T是int时候的代码,这就是特化:
|
||
template <> class AddFloatOrMulInt<int>
|
||
{
|
||
public:
|
||
static int Do(int a, int b)
|
||
{
|
||
return a * b;
|
||
}
|
||
};
|
||
|
||
// 再次,我们要指定T是float时候的代码:
|
||
template <> class AddFloatOrMulInt<float>
|
||
{
|
||
public:
|
||
static float Do(float a, float b)
|
||
{
|
||
return a * b;
|
||
}
|
||
};
|
||
|
||
void foo()
|
||
{
|
||
float a(0), b(1);
|
||
float c = AddFloatOrMulInt<float>::Do(a, b);
|
||
}
|
||
}
|
||
|
||
namespace _2_2_3
|
||
{
|
||
template <typename T> class TypeToID
|
||
{
|
||
public:
|
||
static int const ID = -1;
|
||
};
|
||
|
||
class B {};
|
||
|
||
template <> class TypeToID<void ()>; // 函数的TypeID
|
||
template <> class TypeToID<int[3]>; // 数组的TypeID
|
||
template <> class TypeToID<int (int[3])>; // 这是以数组为参数的函数的TypeID
|
||
template <> class TypeToID<int (B::*[3])(void*, float[2])>; // 我也不知道这是什么了,自己看着办吧。
|
||
|
||
template <> class TypeToID<int const * volatile * const volatile>;
|
||
}
|
||
|
||
namespace _2_2_4
|
||
{
|
||
template <typename T> struct X {};
|
||
|
||
template <typename T> struct Y
|
||
{
|
||
typedef X<T> ReboundType;
|
||
#if WRONG_CODE_ENABLED
|
||
typedef typename X<T>::MemberType MemberType;
|
||
typedef WTF MemberType3;
|
||
#endif
|
||
|
||
static void foo()
|
||
{
|
||
X<T> instance0;
|
||
typename X<T>::MemberType instance1;
|
||
WTF instance2
|
||
大王叫我来巡山 - + &
|
||
}
|
||
};
|
||
|
||
void foo()
|
||
{
|
||
#if WRONG_CODE_ENABLED
|
||
Y<int>::foo();
|
||
Y<float>::foo();
|
||
#endif
|
||
}
|
||
}
|
||
|
||
namespace _2_3_3 {
|
||
struct A;
|
||
template <typename T>
|
||
struct X
|
||
{
|
||
void foo(T v) {
|
||
A a;
|
||
a.v = v;
|
||
}
|
||
};
|
||
|
||
struct A
|
||
{
|
||
int v;
|
||
};
|
||
|
||
int foo2()
|
||
{
|
||
X<int> x;
|
||
x.foo(5);
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
// 1.4 Specialization, Partial Specialization, Full Specialization
|
||
namespace _1_4
|
||
{
|
||
// Prototype of Templates I: Single Parameter
|
||
template <typename T> class ClassD
|
||
{
|
||
int a;
|
||
};
|
||
|
||
// Specialization: Write a pattern for matching
|
||
template <> class ClassD<int> // 1. template <> 2. ClassD<int>
|
||
{
|
||
int b;
|
||
};
|
||
|
||
template <> class ClassD<float>
|
||
{
|
||
int c;
|
||
};
|
||
|
||
// Partial-Specialization: A partial pattern for matching
|
||
template <typename T> class ClassD<T*> // 1. template <typename T> 2. ClassD<T*>
|
||
{
|
||
int d;
|
||
};
|
||
|
||
template <> class ClassD<int*> // 1. template <> 2. ClassD<T*>
|
||
{
|
||
int e;
|
||
};
|
||
|
||
// Question:
|
||
|
||
// ClassD<int>::?
|
||
// ClassD<float>::?
|
||
// ClassD<double>::?
|
||
// ClassD<double*>::?
|
||
// ClassD<int*>::?
|
||
// ClassD<int const*>::?
|
||
|
||
// Prototype of Templates II: Multiple Parameter
|
||
template <typename T, typename U> class ClassE
|
||
{
|
||
int a;
|
||
};
|
||
|
||
template <typename T, typename U> class ClassE<T, U*>
|
||
{
|
||
int b;
|
||
};
|
||
|
||
template <typename T> class ClassE<T, int>
|
||
{
|
||
int c;
|
||
};
|
||
|
||
template <typename T> class ClassE<T, int*>
|
||
{
|
||
int d;
|
||
};
|
||
|
||
template <typename U> class ClassE<int, U>
|
||
{
|
||
int e;
|
||
};
|
||
|
||
template <> class ClassE<int, int>
|
||
{
|
||
int f;
|
||
};
|
||
|
||
// Question:
|
||
|
||
// ClassE<float, double>::?
|
||
// ClassE<float, int>::?
|
||
// ClassE<int, float>::?
|
||
// ClassE<int, int*>::?
|
||
// ClassE<int, int>::?
|
||
|
||
// Member function specialization
|
||
template <typename T>
|
||
class ClassF
|
||
{
|
||
public:
|
||
void foo();
|
||
};
|
||
|
||
template <typename T>
|
||
void ClassF<T>::foo()
|
||
{
|
||
}
|
||
|
||
template <>
|
||
void ClassF<int>::foo()
|
||
{
|
||
}
|
||
|
||
void foo()
|
||
{
|
||
ClassF<int>().foo();
|
||
ClassF<float>().foo();
|
||
}
|
||
}
|
||
|
||
// 2.1 Function Specialization
|
||
namespace _2_1
|
||
{
|
||
// Overload is enabled but no partial-specialization
|
||
template <typename T> void foo(T const& x) {}
|
||
template <typename T> void foo(T& y) {}
|
||
void foo(int&) {}
|
||
void foo(int) {}
|
||
|
||
// Specialization or Overloading
|
||
template <> void foo<bool>(bool const& x) {}
|
||
|
||
// Overloading
|
||
template <typename T> void foo(T const*) {}
|
||
|
||
template <typename T, typename U> void foo2(T const&, U const&);
|
||
|
||
#if WRONG_CODE_ENABLED
|
||
template <typename U> void foo2<int, U>(int const&, U const&);
|
||
template <typename T, typename U> void foo2<T, U>(int const&, U const&);
|
||
#endif
|
||
|
||
// Overloading - Looks like partial specification
|
||
template <typename U> void foo2(int const&, U const&);
|
||
template <typename T, typename U> void foo2(T const*, U const&);
|
||
|
||
// Don't forgot
|
||
// T foo(...);
|
||
|
||
// Specialize types which cannot be inferred by parameter
|
||
template <typename UninferableT, typename InferableT>
|
||
UninferableT foo3(InferableT const&) { return UninferableT(); }
|
||
|
||
void test()
|
||
{
|
||
|
||
int x = 5;
|
||
float y = 10.0f;
|
||
foo(y);
|
||
int const z = 5;
|
||
foo(z);
|
||
foo(true);
|
||
foo3<int>(0.0f); // Specialize types which is uninferable.
|
||
|
||
#if WRONG_CODE_ENABLED
|
||
foo(3); // Ambigous
|
||
foo(x); // Ambigous
|
||
#endif
|
||
}
|
||
}
|
||
|
||
// 2.2 Example: Derived from template.
|
||
namespace _2_2
|
||
{
|
||
template <typename T>
|
||
class ClassA
|
||
{
|
||
T x;
|
||
};
|
||
|
||
template <typename T>
|
||
class ClassB
|
||
{
|
||
T* x;
|
||
};
|
||
|
||
template <typename T>
|
||
class ClassC: public ClassB<T>
|
||
{
|
||
T* x;
|
||
};
|
||
|
||
ClassC<int> a;
|
||
|
||
#if WRONG_CODE_ENABLED
|
||
class ClassC: public ClassA<ClassC>
|
||
{
|
||
};
|
||
#endif
|
||
|
||
class ClassD: public ClassB<ClassD>
|
||
{
|
||
};
|
||
|
||
// ClassC =??= ClassD
|
||
}
|
||
|
||
// 3.1 Meta Switch-Case/If-Then-Else via Specialization
|
||
namespace _3_1
|
||
{
|
||
bool equal(int a, int b)
|
||
{
|
||
return a == b;
|
||
}
|
||
|
||
// meta functions:
|
||
// bool equal0(TypeA, TypeB)
|
||
// {
|
||
// return false;
|
||
// }
|
||
// bool equal1(TypeA, TypeA)
|
||
// {
|
||
// return true;
|
||
// }
|
||
// equal(A, A) == equal1(A, A) == true
|
||
// euqla(A, B) == equal0(A, B) == false
|
||
template <typename T, typename U>
|
||
class Equal
|
||
{
|
||
public:
|
||
static bool const value = false;
|
||
};
|
||
|
||
template <typename T>
|
||
class Equal<T, T>
|
||
{
|
||
public:
|
||
static bool const value = true;
|
||
};
|
||
|
||
bool x = Equal<int, float>::value;
|
||
bool y = Equal<int, int>::value;
|
||
}
|
||
|
||
// 3.2 SFINAE: Substitution Failure Is Not An Error.
|
||
namespace _3_2
|
||
{
|
||
class ClassA
|
||
{
|
||
};
|
||
|
||
template <int Sz> struct Mark
|
||
{
|
||
char _[Sz];
|
||
};
|
||
|
||
#if WRONG_CODE_ENABLED
|
||
template <typename T>
|
||
Mark<1> TestIncrementAdd(T const& v)
|
||
{
|
||
T tmp = v;
|
||
++tmp;
|
||
return Mark<1>();
|
||
}
|
||
|
||
template <typename T>
|
||
Mark<2> TestIncrementAdd(T const& v)
|
||
{
|
||
return Mark<2>();
|
||
}
|
||
|
||
bool a = TestIncrementAdd( ClassA() ) ) == sizeof(Mark<1>);
|
||
#endif
|
||
|
||
// Right case: From Wiki
|
||
class ClassB
|
||
{
|
||
public:
|
||
typedef int Marker;
|
||
};
|
||
|
||
template <typename T> void test(typename T::Marker) { }
|
||
template <typename T> void test(T) { }
|
||
|
||
void DoTest()
|
||
{
|
||
test<ClassB>(10); // Call #1.
|
||
test<int>(10); // Call #2. SFINAE for test(T::Marker).
|
||
}
|
||
}
|
||
|
||
// 3.3 Application: Type Traits
|
||
namespace _3_3
|
||
{
|
||
template <typename T, typename U> class is_same;
|
||
|
||
|
||
template <typename B, typename D> class is_base_of;
|
||
// is_base_of
|
||
// 1. B is class, D is also class.
|
||
// 2. D* could be convert to B*
|
||
// 3. B != D
|
||
|
||
// Fundamentals
|
||
typedef char Accepted;
|
||
typedef int Rejected;
|
||
|
||
class B
|
||
{
|
||
};
|
||
|
||
class D: public B
|
||
{
|
||
};
|
||
|
||
class D2: public D
|
||
{
|
||
};
|
||
|
||
// Type is a class
|
||
template <typename T>
|
||
class is_class
|
||
{
|
||
private:
|
||
// SFINAE
|
||
template <typename U> static Accepted test( int U::* );
|
||
template <typename U> static Rejected test(...);
|
||
|
||
public:
|
||
static const bool value = sizeof( test<T>(0) ) == sizeof(Accepted);
|
||
};
|
||
|
||
bool a = is_class<int>::value;
|
||
bool b = is_class<B>::value;
|
||
|
||
// B* could be convert to D*
|
||
template <typename Source, typename Dest>
|
||
class Convertible
|
||
{
|
||
private:
|
||
// Not SFINAE
|
||
static Accepted test(Dest*);
|
||
static Rejected test(...);
|
||
public:
|
||
static const bool value = sizeof( test(static_cast<Source*>(NULL)) ) == sizeof(Accepted);
|
||
};
|
||
|
||
bool c = Convertible<B, D>::value;
|
||
bool d = Convertible<D, B>::value;
|
||
bool e = Convertible<B, int>::value;
|
||
|
||
// B != D
|
||
using _3_1::Equal;
|
||
|
||
template <typename Base, typename Derived>
|
||
class is_base_of
|
||
{
|
||
public:
|
||
static bool const value =
|
||
is_class<Base>::value &&
|
||
is_class<Derived>::value &&
|
||
Convertible<Base, Derived>::value &&
|
||
!Equal<Base, Derived>::value;
|
||
};
|
||
|
||
bool f = is_base_of<B, D2>::value;
|
||
bool g = is_base_of<D2, D>::value;
|
||
bool h = is_base_of<B, int>::value;
|
||
bool i = is_base_of<float, int>::value;
|
||
|
||
// Questions:
|
||
// remove_reference
|
||
// remove_pointer
|
||
// remove all qualifiers
|
||
}
|
||
|
||
// 3.4 Application: "Recursive" and Meta-Programming
|
||
namespace _3_4
|
||
{
|
||
// sum a, a+1, ..., b-1, b
|
||
int basic_algo(int a, int b)
|
||
{
|
||
int result = 0;
|
||
for (int i = a; i <= b; ++i)
|
||
{
|
||
result += i;
|
||
}
|
||
return result;
|
||
}
|
||
|
||
// Template could not support variable
|
||
|
||
// sum [a, b] without variable
|
||
int recursive_algo(int a, int b)
|
||
{
|
||
if (a == b)
|
||
{
|
||
return b;
|
||
}
|
||
return a + recursive_algo(a+1, b);
|
||
}
|
||
|
||
// Translate to meta-programming
|
||
template <int a, int b>
|
||
class MetaSum
|
||
{
|
||
public:
|
||
static int const value = MetaSum<a+1, b>::value + a;
|
||
};
|
||
|
||
template <int a>
|
||
class MetaSum<a, a>
|
||
{
|
||
public:
|
||
static int const value = a;
|
||
};
|
||
|
||
int a = MetaSum<1, 10>::value;
|
||
}
|
||
|
||
// 3.5 Application: Meta-Fibonacci
|
||
namespace _3_5
|
||
{
|
||
template <int Index>
|
||
class Fibonacci
|
||
{
|
||
public:
|
||
static int const value = Fibonacci<Index - 1>::value + Fibonacci<Index - 2>::value;
|
||
};
|
||
|
||
template <>
|
||
class Fibonacci<0>
|
||
{
|
||
public:
|
||
static int const value = 0;
|
||
};
|
||
|
||
template <>
|
||
class Fibonacci<1>
|
||
{
|
||
public:
|
||
static int const value = 1;
|
||
};
|
||
|
||
int a = Fibonacci<8>::value;
|
||
}
|
||
|
||
// 4 Directive word: typename and template
|
||
namespace _4
|
||
{
|
||
// typename T::type x;
|
||
// ??? typename ???
|
||
|
||
// typename T::template U<type> x;
|
||
// ??? template ???
|
||
|
||
class ClassA
|
||
{
|
||
public:
|
||
typedef int NestedType;
|
||
};
|
||
|
||
class ClassB
|
||
{
|
||
public:
|
||
typedef ClassA::NestedType NestedType;
|
||
};
|
||
|
||
template <typename T>
|
||
class ClassC
|
||
{
|
||
public:
|
||
#if WRONG_CODE_ENABLED
|
||
typedef T::NestedType NestedType;
|
||
#endif
|
||
typedef typename T::NestedType NestedType;
|
||
typedef typename std::vector<T>::iterator iterator;
|
||
};
|
||
|
||
class ClassD
|
||
{
|
||
public:
|
||
template <typename U, typename V> class NestedType;
|
||
};
|
||
|
||
template <typename T>
|
||
class ClassE
|
||
{
|
||
public:
|
||
template <typename U> class NestedType;
|
||
};
|
||
|
||
template <typename T, typename U>
|
||
class ClassF
|
||
{
|
||
#if WRONG_CODE_ENABLED
|
||
typedef typename T::NestedType<U> NestedType;
|
||
#endif
|
||
typedef typename T::template NestedType<U, int> NestedType;
|
||
typedef typename ClassE<T>::template NestedType<U> NestedType2;
|
||
};
|
||
|
||
ClassC<ClassB> a;
|
||
ClassF<ClassD, float> b;
|
||
}
|
||
|
||
// 5.1 How to Construct Meta Operators
|
||
namespace _5_1
|
||
{
|
||
// Expression = Value/Data Structure + Operator/Operations
|
||
|
||
// Value in Templates:
|
||
// Integral Constant (bool, char, unsigned, ...)
|
||
// Type (typename)
|
||
|
||
// 1. Trick: Constant <--> Type
|
||
template <int i>
|
||
class int_
|
||
{
|
||
public:
|
||
static int const value = i;
|
||
};
|
||
|
||
int a = int_<5>::value;
|
||
|
||
// This trick could work with overloading
|
||
template <typename T>
|
||
void Do(T* obj, int_<2>)
|
||
{
|
||
}
|
||
|
||
template <typename T>
|
||
void Do(T* obj, int_<1>)
|
||
{
|
||
}
|
||
|
||
void foo()
|
||
{
|
||
Do( static_cast<int*>(nullptr), int_<1>() );
|
||
}
|
||
|
||
template <typename T, int i> void DoAnotherWay(T* obj)
|
||
{
|
||
}
|
||
|
||
// Boolean is more useful than integral in general.
|
||
template <bool v>
|
||
class bool_
|
||
{
|
||
public:
|
||
static bool const value = v;
|
||
};
|
||
|
||
typedef bool_<true> true_;
|
||
typedef bool_<false> false_;
|
||
|
||
#if WRONG_CODE_ENABLED
|
||
// Aha, function cannot support partial specialization.
|
||
template <typename T> void DoAnotherWay<T, 1>(T* obj) {}
|
||
template <typename T> void DoAnotherWay<T, 2>(T* obj) {}
|
||
#endif
|
||
|
||
// 2. Operators:
|
||
// add
|
||
|
||
template <typename T, typename U>
|
||
class add_
|
||
{
|
||
public:
|
||
typedef int_<T::value + U::value> type;
|
||
static int const value = type::value;
|
||
};
|
||
|
||
#if WRONG_CODE_ENABLED
|
||
// conflict
|
||
template <int x, int y>
|
||
class add_
|
||
{
|
||
public:
|
||
typedef int_<x+y> type;
|
||
static int const value = type::value;
|
||
};
|
||
#endif
|
||
template <int x, int y>
|
||
class add_c
|
||
{
|
||
public:
|
||
typedef int_<x+y> type;
|
||
static int const value = type::value;
|
||
};
|
||
|
||
typedef add_< int_<2>, int_<3> >::type sum;
|
||
int b = sum::value;
|
||
|
||
typedef add_< int_<2>, int_<3> >::type sum_c;
|
||
int c = sum_c::value;
|
||
|
||
// another solution
|
||
template <typename T, typename U>
|
||
class add2_: public int_<T::value+U::value>
|
||
{
|
||
};
|
||
int d = add2_< int_<2>, int_<3> >::value;
|
||
|
||
// Other operators: sub, not, or, and ...
|
||
}
|
||
|
||
// 5.2 Example of Meta Programming: Meta-Vector
|
||
namespace _5_2
|
||
{
|
||
// Array: elem[count]
|
||
// Meta Array ?
|
||
|
||
// Recursively Definition
|
||
// 'Null' terminated
|
||
template <typename HeadT, typename TailT>
|
||
class pair_
|
||
{
|
||
typedef HeadT head;
|
||
typedef TailT tail;
|
||
};
|
||
|
||
class Nil;
|
||
|
||
// Try Use It to Definition
|
||
typedef pair_< int, pair_<float, pair_<double, Nil> > > vector_3;
|
||
|
||
template <typename T0, typename T1 = Nil, typename T2 = Nil, typename T3 = Nil>
|
||
class make_vector_
|
||
{
|
||
typedef pair_< T0, make_vector_<T1, T2, T3> > type;
|
||
};
|
||
|
||
template <>
|
||
class make_vector_<Nil, Nil, Nil, Nil>
|
||
{
|
||
typedef Nil type;
|
||
};
|
||
|
||
template <typename T0, typename T1 = Nil, typename T2 = Nil, typename T3 = Nil>
|
||
class vector_: public make_vector_<T0, T1, T2, T3>::type
|
||
{
|
||
};
|
||
|
||
typedef vector_<double, float, int> vector3;
|
||
|
||
// Let's meta-program further
|
||
//
|
||
// push_back ? tip: push_back<Vector, Element>::type
|
||
// pop ?
|
||
// find ?
|
||
// size ?
|
||
}
|
||
|
||
// 6.1 Template-Template Class
|
||
|
||
// 6.2 High order function, closure and STL allocator rebind
|
||
|
||
int _tmain(int argc, _TCHAR* argv[])
|
||
{
|
||
return 0;
|
||
}
|
||
|