В чем разница между чертой и политикой?

У меня есть класс, поведение которого я пытаюсь настроить.

template<int ModeT, bool IsAsync, bool IsReentrant> ServerTraits;

Затем у меня есть сам объект сервера:

template<typename TraitsT>
class Server {...};

Мой вопрос для моего использования выше, мое имя неправильно названо? Является ли мой шаблонный параметр политикой, а не чертой характера?

Когда шаблонный аргумент является чертой против политики?

58

Решение

полисы

Политики — это классы (или шаблоны классов) для вводить поведение в родительский класс, как правило, через наследование. Разлагая родительский интерфейс на ортогональные (независимые) измерения, классы политики формируют строительные блоки более сложных интерфейсов. Часто встречающийся шаблон заключается в предоставлении политик в качестве определяемых пользователем параметров шаблона (или шаблона-шаблона) с предоставленными по умолчанию библиотекой. Примером из стандартной библиотеки являются Распределители, которые являются параметрами шаблона политики всех контейнеров STL

template<class T, class Allocator = std::allocator<T>> class vector;

Здесь Allocator Параметр шаблона (который также является шаблоном класса!) внедряет политику выделения и освобождения памяти в родительский класс std::vector, Если пользователь не предоставляет распределитель, по умолчанию std::allocator<T> используется.

Как типично для основанного на шаблонах полиморфизма, требования интерфейса к классам политики неявный и семантический (основанный на действительных выражениях), а не явный и синтаксический (основанный на определении виртуальных функций-членов).

Обратите внимание, что более поздние неупорядоченные ассоциативные контейнеры имеют более одной политики. В дополнение к обычному Allocator параметр шаблона, они также принимают Hash политика, которая по умолчанию std::hash<Key> функциональный объект. Это позволяет пользователям неупорядоченных контейнеров настраивать их по нескольким ортогональным измерениям (выделение памяти и хеширование).

Черты

Черты являются шаблонами классов для извлечь свойства из универсального типа. Существует два вида черт: однозначные и многозначные. Примеры однозначных признаков — те, что в заголовке <type_traits>

template< class T >
struct is_integral
{
static const bool value /* = true if T is integral, false otherwise */;
typedef std::integral_constant<bool, value> type;
};

Однозначные черты часто используются в Шаблон-метапрограммированием и трюки SFINAE для перегрузки шаблона функции, основанного на условии типа.

Примерами многозначных признаков являются iterator_traits и allocator_traits из заголовков <iterator> а также <memory>соответственно. Поскольку признаки являются шаблонами классов, они могут быть специализированными. Ниже приведен пример специализации iterator_traits за T*

template<T>
struct iterator_traits<T*>
{
using difference_type   = std::ptrdiff_t;
using value_type        = T;
using pointer           = T*;
using reference         = T&;
using iterator_category = std::random_access_iterator_tag;
};

Выражение std::iterator_traits<T>::value_type делает возможным сделать универсальный код для полноценных классов итераторов, пригодных для использования даже для необработанных указателей (так как необработанные указатели не имеют члена value_type).

Взаимодействие между политиками и чертами

При написании ваших собственных универсальных библиотек важно подумать о том, как пользователи могут специализировать ваши собственные шаблоны классов. Однако следует соблюдать осторожность, чтобы пользователи не стали жертвами Одно Правило Определения используя специализации черт, чтобы вводить, а не извлекать поведение. Перефразируя это старый пост Андрей Александреску

Основная проблема заключается в том коде, который не видит специализированный
версия черты будет по-прежнему компилироваться, скорее всего, ссылка, и
иногда может даже бежать. Это потому, что в отсутствие
явная специализация, не специализированный шаблон,
реализация общего поведения, которое работает для вашего особого случая, как
Что ж. Следовательно, если не весь код в приложении видит
То же определение черты, ODR нарушается.

C ++ 11 std::allocator_traits позволяет избежать этих ловушек, поскольку все контейнеры STL могут извлекать свойства только из своих Allocator политика через std::allocator_traits<Allocator>, Если пользователи решат не предоставлять или забыть предоставить некоторые обязательные элементы политики, класс признаков может вмешаться и предоставить значения по умолчанию для этих пропущенных элементов. Так как allocator_traits сам по себе не может быть специализированным, пользователи всегда должны проходить полностью определенную политику выделения ресурсов, чтобы настроить распределение памяти в своих контейнерах, и не может произойти никаких нарушений ODR.

Обратите внимание, что, как писатель библиотеки, можно по-прежнему специализировать шаблоны классов признаков (как это делает STL в iterator_traits<T*>), но хорошей практикой является передача всех пользовательских специализаций через классы политики в многозначные признаки, которые могут извлекать специализированное поведение (как это делает STL в allocator_traits<A>).

ОБНОВИТЬ: Проблемы ODR пользовательских специализаций классов признаков возникают, главным образом, когда признаки используются как глобальные шаблоны классов и вы не можете гарантировать, что все будущие пользователи будут видеть все другие пользовательские специализации. Политики локальные параметры шаблона и содержат все соответствующие определения, позволяющие определять их пользователем без вмешательства в другой код. Локальные параметры шаблона, которые содержат только тип и константы — но без поведенческих функций — могут все еще называться «чертами», но они не будут видны для другого кода, такого как std::iterator_traits а также std::allocator_traits,

81

Другие решения

Я думаю, что вы найдете лучший ответ на свой вопрос в эта книга Андрея Александреску. Здесь я постараюсь дать только краткий обзор. Надеюсь, это поможет.


класс черт это класс, который обычно предназначен для мета-функции, связывающей типы с другими типами или с постоянными значениями, чтобы обеспечить характеристику этих типов. Другими словами, это способ моделирования свойства типов. Механизм обычно использует шаблоны и специализацию шаблонов для определения ассоциации:

template<typename T>
struct my_trait
{
typedef T& reference_type;
static const bool isReference = false;
// ... (possibly more properties here)
};

template<>
struct my_trait<T&>
{
typedef T& reference_type;
static const bool isReference = true;
// ... (possibly more properties here)
};

Метафункция черты my_trait<> выше связывает ссылочный тип T& и постоянное логическое значение false для всех типов T которые не сами ссылки; с другой стороны, он связывает ссылочный тип T& и постоянное логическое значение true для всех типов T тот являются Рекомендации.

Так, например:

int  -> reference_type = int&
isReference = false

int& -> reference_type = int&
isReference = true

В коде мы могли бы утверждать вышеизложенное следующим образом (все четыре строки ниже будут скомпилированы, что означает, что условие, выраженное в первом аргументе для static_assert() доволен)

static_assert(!(my_trait<int>::isReference), "Error!");
static_assert(  my_trait<int&>::isReference, "Error!");
static_assert(
std::is_same<typename my_trait<int>::reference_type, int&>::value,
"Error!");
static_assert(
std::is_same<typename my_trait<int&>::reference_type, int&>::value,
"Err!");

Здесь вы могли видеть, что я использовал стандарт std::is_same<> шаблон, который сам по себе является мета-функцией, которая принимает два, вместо одного введите аргумент. Здесь все может быть сколь угодно сложно.

Хотя std::is_same<> является частью type_traits заголовок, некоторые считают, что шаблон класса является классом черт типа, только если он действует как мета-предикат (таким образом, принимая один параметр шаблона). Однако, насколько мне известно, терминология четко не определена.

Для примера использования класса признаков в стандартной библиотеке C ++ посмотрите, как спроектированы библиотека ввода / вывода и библиотека строк.


политика это что-то немного другое (на самом деле, совсем другое). Обычно подразумевается, что это класс, который определяет поведение другого универсального класса в отношении определенных операций, которые потенциально могут быть реализованы несколькими различными способами (и, следовательно, реализация которых оставлена ​​на усмотрение класса политики).

Например, универсальный класс интеллектуальных указателей может быть спроектирован как шаблонный класс, который принимает политику в качестве параметра шаблона для принятия решения о том, как обрабатывать повторный подсчет — это всего лишь гипотетический, слишком упрощенный и иллюстративный пример, поэтому, пожалуйста, попробуйте абстрагироваться из этого конкретного кода и сосредоточиться на механизм.

Это позволило бы разработчику интеллектуального указателя не делать жестко заданного обязательства относительно того, должны ли модификации счетчика ссылок выполняться поточно-ориентированным образом:

template<typename T, typename P>
class smart_ptr : protected P
{
public:
// ...
smart_ptr(smart_ptr const& sp)
:
p(sp.p),
refcount(sp.refcount)
{
P::add_ref(refcount);
}
// ...
private:
T* p;
int* refcount;
};

В многопоточном контексте клиент может использовать создание шаблона смарт-указателя с политикой, которая реализует поточно-ориентированные приращения и уменьшения счетчика ссылок (предполагается, что Windows Platformmed):

class mt_refcount_policy
{
protected:
add_ref(int* refcount) { ::InterlockedIncrement(refcount); }
release(int* refcount) { ::InterlockedDecrement(refcount); }
};

template<typename T>
using my_smart_ptr = smart_ptr<T, mt_refcount_policy>;

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

class st_refcount_policy
{
protected:
add_ref(int* refcount) { (*refcount)++; }
release(int* refcount) { (*refcount)--; }
};

template<typename T>
using my_smart_ptr = smart_ptr<T, st_refcount_policy>;

Таким образом, разработчик библиотеки предоставил гибкое решение, способное предложить лучший компромисс между производительностью и безопасностью («Вы не платите за то, что не используете»).

23

Если вы используете ModeT, IsReentrant и IsAsync для управления поведением Сервера, тогда это политика.

В качестве альтернативы, если вы хотите описать характеристики сервера для другого объекта, вы можете определить класс признаков следующим образом:

template <typename ServerType>
class ServerTraits;

template<>
class ServerTraits<Server>
{
enum { ModeT = SomeNamespace::MODE_NORMAL };
static const bool IsReentrant = true;
static const bool IsAsync = true;
}
3

Вот несколько примеров, поясняющих комментарий Алекса Чемберлена:

Типичным примером класса признаков является std :: iterator_traits. Допустим, у нас есть некоторый шаблонный класс C с функцией-членом, который принимает два итератора, перебирает значения и каким-то образом накапливает результат. Мы хотим, чтобы стратегия накопления также была определена как часть шаблона, но для ее достижения мы будем использовать политику, а не черту.

template <typename Iterator, typename AccumulationPolicy>
class C{
void foo(Iterator begin, Iterator end){
AccumulationPolicy::Accumulator accumulator;
for(Iterator i = begin; i != end; ++i){
std::iterator_traits<Iterator>::value_type value = *i;
accumulator.add(value);
}
}
};

Политика передается в наш шаблонный класс, в то время как признак является производным от параметра шаблона. То, что у вас есть, больше похоже на политику. Есть ситуации, когда черты являются более подходящими, и где политика является более подходящей, и часто тот же самый эффект может быть достигнут с любым из методов, приводящих к некоторой дискуссии, которая является наиболее выразительной.

1
По вопросам рекламы [email protected]