четверг, 31 мая 2007 г.

С++09 - новый стандарт, новые возможности. Часть 2.

Продолжая тему, начатую в предыдущем посте, рассмотрим следующее нововведение в стандарте C++09. Итак, встречайте:

Шаблоны с переменным количеством аргументов (Variadic templates (aka "type varargs" for templates) - [N2242], [N2087])

Вы наверное знаете, что существуют функции типа printf, использующие переменное количество аргументов. Естественно, рано или поздно настанет время, когда вам понадобится возможность сделать подобное для шаблона, у которого количество параметров тоже будет переменным. Изначально в новой версии стандарта предполагалось использовать так называемые кортежи (tuples), которые могут быть определены для какого-то фиксированного числа шаблонных аргументов:

// Черновой вариант в C++09 до введения variadic templates
// (некоторые параметры по умолчанию жестко заданы)
//
template<
class T1 = unspecified ,
class T2 = unspecified ,
... ,
class TN = unspecified
> class tuple;

template<class T1, class T2, ..., class TN>
tuple<V1, V2, ..., VN> make_tuple( const T1&, const T2& , ..., const TN& );

Данное объявление выглядит всё более и более зловещим при увеличении N. И что делать, если нам понадобится больше типов, чем содержится в объявлении? К счастью, с появлением variadic templates в C++09 все становится гораздо проще и понятнее:

//
template<class... Types> class tuple;

template<class... Types>
tuple<VTypes...> make_tuple( Types&... );

Данный вариант гораздро проще для определения и работает с любым количеством аргументов. Рассмотрим пример:

// Абсолютно корректный код в C++09
//
template<typename... Mixins>
class X : public Mixins...
{
public:
X( const Mixins&... mixins ) : Mixins(mixins)... { }
};

class A { };
class B { };
class C { };

X<A, B, C> x;

Для типа X<A, B, C> компилятор сгенерирует примерно следующий код:

class X<A, B, C> : public A, public B, public C
{
public:
X( A const& __a, B const& __b, C const& __c ) : A(__a), B(__b), C(__c) { }
};

Продолжение следует...

четверг, 24 мая 2007 г.

C++09 - новый стандарт, новые возможности

В своём блоге Герб Саттер приводит примеры нескольких новых особенностей C++, которые вошли в черновой вариант стандарта C++09. Я решил сделать небольшой обзор наиболее интересных из них.

Начнём с шаблонных алиасов (Template aliases (aka typedef templates, generalized typedefs) [N2258]) . Данная возможность позволяет зафиксировать часть параметров шаблона.

// Не является правильным в C++, но хотелось бы...
//
template<typename T>
typedef std::map<std::string, T> Registry;

// Тогда можно было бы написать следующее:
//
Registry<employee> employeeRoster;
Registry<void(*)(int)> callbacks;
Registry<classfactory*> factories;

В C++09 вводится более обобщенная поддержка алиасов:

// Разрешено в C++09
//
template<typename T>
using Registry = std::map<std::string, T>;

// Теперь это работает, как и задумывалось:
//
Registry<Employee> employeeRoster;
Registry<void (*)(int)> callbacks;
Registry<ClassFactory*> factories;

Пока для создания подобного поведения можно использовать шаблонную стуктуру-обёртку (на примере смарт-указателя, инкапсулирующего класс Loki::SmartPtr, который использует несколько стратегий в качестве параметров шаблона):

template<typename T>
struct shared_ptr
{
typedef Loki::SmartPtr
<
T, // T может быть любым, остальные параметры фиксированы
RefCounted, NoChecking, false, PointsToOneObject, SingleThreaded, SimplePointer<T>
>
type;
};

shared_ptr<int>::type p; // пример использования, “::Type” - выглядит зловеще

Одной из основных причин (но и не только) для введения в новом стандарте шаблонных алиасов является удобство в использовании. Теперь при написании подобного кода не придётся явно указывать набор причудливых параметров, которые невозможно спрятать с помощью обычного алиаса:

// Разрешено в C++09
//
template<typename T>
using shared_ptr =
Loki::SmartPtr<
T,
RefCounted, NoChecking, false, PointsToOneObject, SingleThreaded, SimplePointer<T>
>;

shared_ptr<int> p; // вариант в C++09

Следует заметить, что применение новых алиасов не ограничивается только шаблонами:

// Верно в C++98 and C++09
//
typedef int Size;
typedef void (*handler_t)(int);

// Верно в C++09
//
using Size = int;
using handler_t = void (*)(int);

Для таких типов, как указатели на функции, это особенность новой формы "using" весьма важна, поскольку она является более простой в использовании и наглядной, чем typedef.

Продолжение следует...