Техника «другое» очень распространена в C ++. Класс параметров обычно называется « класс черты ».
Это путь (почему он вызывает у вас чувство беспокойства?). Он широко используется в библиотеках Boost и других библиотеках C ++. Даже стандартная библиотека использует его, например, в классе std::basic_string
.
Не менее устоявшейся альтернативой являются метафункции. По своей сути метафункция - это «функция», которая работает с типами, а не с объектами. Поэтому, когда функция принимает аргументы-значения и возвращает значение, метафункции принимают аргументы шаблона и «возвращают» тип:
template <typename T>
struct identity {
typedef T type;
};
Метафункция используется («вызывается») как определение обычного типа.
typedef identity<int>::type mytype; // or
identity<int>::type x;
Не очень полезно в этом случае. Но рассмотрим следующую общую метафункцию:
template <typename T>
struct remove_const {
typedef T type;
};
template <typename T>
struct remove_const<T const> {
typedef T type;
};
Это можно использовать, чтобы сделать произвольный тип (в частности, аргумент шаблона) неконстантным. На самом деле это тип, который я сейчас использую в проекте: у меня есть класс, который принимает как константные, так и неконстантные типы и предлагает соответствующие интерфейсы. Однако внутренне Мне нужно хранить неконстантную ссылку. Просто я просто использую следующий код в своем классе:
typename remove_const<T>::type& _reference;
(typename
требуется, потому что T
является аргументом шаблона, и это делает remove_const<T>::type
зависимый тип . В приведенном выше примере кода на самом деле пропущено довольно много обязательных typename
s - он не будет компилироваться на нескольких современных компиляторах!)
Теперь, как применить это к вашей проблеме?
Создайте два пустых типа маркера , которые указывают, используются ли ваши типы на встроенном устройстве или на совместимом компиляторе:
struct Embedded { };
struct Compliant { };
Теперь вы можете определять свои классы в терминах этих, например ::
template<typename Spec>
class ThreadedMessageAcceptor
{
typedef Message< Spec > message_type;
typedef MessageDestination< Spec > destination_type;
typedef typename Allocator< destination_type, Spec >::type allocator_type;
typedef typename ContainerB< destination_type, allocator_type, Spec >::type destinations_type;
};
Здесь Spec
будет либо Compliant
, либо Embedded
. Чтобы использовать его на совместимом со стандартами компиляторе, напишите:
ThreadedMessageAcceptor<Compliant> x;
В классе используются следующие метафункции:
template <typename T, typename Spec>
struct Allocator { };
template <typename T, typename Alloc, typename Spec>
struct ContainerB { };
Вы должны помнить, чтобы специализировать их в соответствии с вашими целевыми характеристиками, например ::1010 *
template <typename T>
struct Allocator<T, Compliant> {
typedef std::allocator<T> type;
};
template <typename T, typename Alloc>
struct ContainerB<T, Alloc, Compliant> {
typedef std::vector<T, Alloc> type;
};
Это уже показывает, что метафункция может иметь произвольно много аргументов, кроме Spec
(который я поставил последним по своей прихоти - но его размещение должно быть последовательным).
Конечно, это больше кода, чем при использовании одного класса признаков, но он имеет меньшую сплоченность, логически разделяет проблемы и его легче использовать повторно.