C++ templates
Синтаксис
template < typename T >
void f(const T& value)
{
std::cout << "Value is: " << value << std::endl;
}
template < typename T, size_t n >
struct Array
{
T data[n];
};
Примеры использования
template < typename T >
class auto_ptr
{
typedef auto_ptr MyType;
T* _ptr;
auto_ptr(const MyType& other);
MyType& operator = (const MyType& other);
public:
auto_ptr(T* const ptr) : _ptr(ptr) { }
~auto_ptr() { delete _ptr; }
T& operator *() const { return *_ptr; }
T* operator ->() const { return _ptr; }
};
Примеры использования
struct A
{
int a;
};
int main()
{
auto_ptr<A> p_a = new A;
(*p_a).a = 9;
int i = p_a->a;
return 0;
}
smart pointers
• std::auto_ptr – лучше не использовать
• _com_ptr_t – для COM-интерфейсов
• boost::scoped_ptr – некопируемый
• boost::shared_ptr – копируемый, использует подсчёт ссылок
• boost::weak_ptr – решает проблему «кольцевых ссылок»
boost:: scoped_ptr
template<class T> class scoped_ptr // noncopyable
{
T * ptr;
scoped_ptr(scoped_ptr const &); // prohibited
scoped_ptr & operator=(scoped_ptr const &); // prohibited
void operator==( scoped_ptr const& ) const; // prohibited
void operator!=( scoped_ptr const& ) const; // prohibited
public:
explicit scoped_ptr(T * p = 0); // never throws
explicit scoped_ptr(std::auto_ptr<T> p); // never throws
~scoped_ptr(); // never throws
T & operator*() const; // never throws
T * operator->() const; // never throws
operator bool () const; // never throws
bool operator! () const; // never throws
T * get() const; // never throws
void reset(T * p = 0); // never throws
void swap(scoped_ptr & b); // never throws
};
std::auto_ptr
template < class _Ty > class auto_ptr
{
public:
template<class _Other> operator auto_ptr<_Other>()
{ return (auto_ptr<_Other>(*this)); }
template<class _Other> auto_ptr<_Ty>& operator = (auto_ptr<_Other>& _Right)
{ reset(_Right.release()); return (*this); }
template<class _Other> auto_ptr(auto_ptr<_Other>& _Right) : _Myptr(_Right.release())
{}
_Ty *release()
{
_Ty *_Tmp = (_Ty *)_Myptr;
_Myptr = 0;
return (_Tmp);
}
...
};
Недостатки глобальных переменных
• Объекты создаются всегда, даже если они не
используются
• Порядок инициализации в общем случае
неизвестен
• Объекты не разрушаются до завершения
программы
• Проблемы с исключениями в конструкторах
объектов
Singleton
template < typename T >
class Singleton
{
public:
static T& Instance()
{
static T instance;
return instance;
}
static const T& ConstInstance()
{
return const_cast<const T&>(Instance());
}
};
Singleton
class A
{
private:
friend class Singleton<A>;
A() {}
public:
int a;
};
int main()
{
Singleton<A>::Instance().a = 3;
std::cout << Singleton<A>::ConstInstance().a << std::endl;
return 0;
}
Частичная специализация шаблонов
template < typename T >
T MaxValue();
template < >
int MaxValue<int>()
{ return INT_MAX; }
template < >
float MaxValue<float>()
{ return FLT_MAX; }
int main()
{
std::cout << "Max int value = " << MaxValue<int>() << std::endl;
std::cout << "Max float value = " << MaxValue<float>() << std::endl;
return 0;
}
Compile time check
template <bool b> struct CompileTimeError;
template <> struct CompileTimeError<true>{};
CompileTimeError<2 == 4> ERROR_assert_failed;
#define STATIC_CHECK(expr, msg) \
{ CompileTimeError<expr> ERROR_##msg; (void)ERROR_##msg; }
int main()
{
STATIC_CHECK(2 == 5, assert_failed)
return 0;
}
Шаблонные шаблонные параметры
template < typename T, template <typename> class Creator = NewCreator >
class Singleton
{
public:
typedef T* InstancePtr;
private:
static InstancePtr _instancePtr;
static void Destroy(void)
{ if(_instancePtr != NULL) Creator<T>::Destroy(_instancePtr); }
public:
static T& Instance()
{
if(_instancePtr == NULL)
{ _instancePtr = Creator<T>::Create(); atexit(&Singleton::Destroy); }
return *_instancePtr;
}
};
template < typename T, template <typename> class C>
typename Singleton<T, C>::InstancePtr Singleton<T, C>::_instancePtr = NULL;
Шаблонные шаблонные параметры
template <class T> struct MallocCreator
{
static T* Create()
{
void* p = std::malloc(sizeof(T));
if (!p) return 0;
return new(p) T;
}
static void Destroy(T* p)
{ p->~T(); std::free(p); }
};
template <class T> struct NewCreator
{
static T* Create() { return new T; }
static void Destroy(T* p) { delete p; }
};
Недостатки указателей на функции
• Нет информации о типах аргументов и
возвращаемого значения
• Указатель на функцию необходимо перед
вызовом проверять на NULL
• Есть возможность привести указатель на
функцию к любому указателю
• Разный синтаксис вызова для указателей на
функции и указателей на методы
Функторы, простая реализация
template < typename Signature >
class function;
template < typename R, typename P >
class function < R (P) >
{
typedef function MyType;
typedef R (*Signature)(P);
Signature _func;
MyType& operator = (const MyType&);
public:
function(Signature func) : _func(func) { }
function(const MyType& other): _func(other._func) { }
R operator () (P p)
{ return _func(p); }
};
Функторы, простая реализация
template <typename ObjType, typename R, typename P1, typename P2>
class function < R (ObjType::*) (P1, P2) >
{
typedef function MyType;
typedef R (ObjType::*Signature)(P1, P2);
Signature _func;
MyType& operator = (const MyType&);
public:
function(Signature func) : _func(func) { }
function(const MyType& other): _func(other._func) { }
R operator () (ObjType& obj, P1 p1, P2 p2)
{ return (obj.*_func)(p1, p2); }
};
Функторы, простая реализация
struct A
{
char C;
A(char c = ‘X') : C(c) { }
void member_func(double i, bool b)
{ if (b) std::cout << C << i << std::endl; }
};
int main()
{
A a;
function< void (A::*)(double, bool) > f(&A::member_func);
f(a, 0.5, true);
function < int (int) > f2(&abs);
std::cout << f2(-123) << std::endl;
return 0;
}
Функторы STL
int main()
{
A a;
std::mem_fun1_t< void, A, double > f = std::mem_fun(&A::member_func);
f(&a, 0.5);
std::mem_fun1_ref_t < void, A, double > f_ref =
std::mem_fun_ref(&A::member_func);
f_ref(a, 0.7);
std::pointer_to_unary_function<int, int> f2 = std::ptr_fun(&abs);
std::cout << f2(-123) << std::endl;
return 0;
}
Недостатки функторов STL
• Уродливый синтаксис
• Отсутствие функторов с большим количеством
параметров
• Отсутствует возможность инициализации
функтора с сигнатурой func1 указателем на func2:
struct B : public A{};
void func1(B*);
void func2(A*);
boost::function
int main()
{
A a;
boost::function < void (A&, double) > f =
&A::member_func;
f(a, 0.5);
boost::function < int (int) > f2 = &abs;
std::cout << f2(-123) << std::endl;
return 0;
}
Применение функторов
void generate_int(int& i)
{ i = rand() % 100 - 50; }
void print_int(int i)
{ std::cout << i << " "; }
int main()
{
std::vector<int> vec(10);
std::for_each(vec.begin(), vec.end(), &generate_int);
std::for_each(vec.begin(), vec.end(), &print_int);
std::cout << std::endl;
std::vector<int> out_vec;
std::transform(vec.begin(), vec.end(), std::back_inserter(out_vec), &abs);
std::for_each(out_vec.begin(), out_vec.end(), &print_int);
std::cout << std::endl;
return 0;
}
Привязывание параметров
int main()
{
std::vector<int> vec(10);
std::for_each(vec.begin(), vec.end(), &generate_int);
std::for_each(vec.begin(), vec.end(), &print_int);
std::cout << std::endl;
vec.erase( std::remove_if(vec.begin(), vec.end(),
std::bind1st(std::less<int>(), 10)), vec.end() );
std::for_each(vec.begin(), vec.end(), &print_int);
std::cout << std::endl;
return 0;
}
Недостатки std::bind1st/2nd
•
•
•
•
Работают только для binary_function
Привязывают только один аргумент
Неудобный синтаксис
Нет возможности привязать ссылку:
void inc (int& n, bool) { ++n; }
// ...
std::bind1st(std::ptr_fun(&inc), i) (true);
boost::bind
bool in_range( int min_val, int max_val, int val )
{ return (val >= min_val) && (val <= max_val); }
int main()
{
// ...
boost::function < bool(int) > pred = boost::bind(&in_range, 5, 15, _1);
vec.erase( std::remove_if(vec.begin(), vec.end(), pred), vec.end() );
// ...
return 0;
}
“Подводные камни” boost::bind
class WindowBase
{
typedef boost::function < void (void) > EventHandler;
EventHandler _onPaint;
protected:
WindowBase(const EventHandler& onPaint) : _onPaint(onPaint) { }
// ...
};
struct MyWindow : public WindowBase
{
MyWindow() : WindowBase(boost::bind(&MyWindow::OnPaint, this)) { }
void OnPaint() { }
};
MyWindow CreateMyGreatWindow()
{ return MyWindow(); }
int main()
{
MyWindow wnd = CreateMyGreatWindow();
return 0;
}
Преимущества использования
шаблонов C++
• Шаблоны решают проблему дублирования
кода
• Зачастую шаблоны позволяют избавиться от
динамической диспечеризации и повысить
скорость работы приложения
• Позволяют отследить большую часть ошибок
на этапе компиляции
• Использование стратегий позволяет не писать
сложные классы с нуля, а собирать их из
множества меньших
• Можно грабить корованы
Список литературы
•
•
•
•
•
•
•
•
Бьерн Страуструп. Язык программирования С++
Скотт Мейерс. Эффективное использование С++
Скотт Мейерс. Наиболее эффективное использование С++
Скотт Мейерс. Эффективное использование STL
Герб Саттер. Решение сложных задач на C++
Герб Саттер. Новые сложные задачи на C++
Андрей Александреску. Современное проектирование на C++
Герб Саттер. Андрей Александреску. Стандарты
программирования на С++. 101 правило и рекомендация
• Владимир Сорокин. Голубое сало.
© Copyright 2026 Paperzz