Наследование (или член) черты характера

Поймать все черты классов, как std::iterator_traits полезны, отделяя свойства типа от его определения, поэтому, например, свойства могут быть доступны до завершения определения.

Определение черт в дополнение к каждому клиентскому классу само по себе неудобно, потому что черты обычно также имеют место в качестве членов. Вот почему общая реализация std::iterator_traits определяется в терминах членов своего шаблонного аргумента.

template< typename it >
struct iterator_traits {
typedef typename it::category category;
typedef typename it::value_type value_type;
// etc
};

Разве не проще и меньше работы для компилятора, вместо этого использовать наследование?

template< typename t >
struct t_traits : public t {
t_traits() = delete; // Prevent runtime instances.
};

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

Кажется бессмысленным писать много повторяющегося кода для определения класса мета-контейнера, в идиоме, которая даже не гарантирует предотвращения такого злоупотребления, как создание во время выполнения.


Или, может быть, это полностью назад. В дополнение к std::iterator_traits у нас также есть std::iteratorпсевдо-абстрактный базовый класс, состоящий в основном из одинаковых членов. Такая избыточность является запахом кода. Разве не было бы лучше, если бы пользовательские итераторы выглядели так?

template<>
struct iterator_traits< struct my_iterator > {
typedef random_access_iterator_tag category;
typedef foo value_type;
...
};
struct my_iterator : iterator_traits< struct my_iterator > {
...
};

(Ради аргумента давайте проигнорируем тот факт, что фактический std::iterator_traits специализация должна быть объявлена ​​в namespace std, Я пытаюсь сделать знакомую иллюстрацию того, что может произойти в коде пользователя.)

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

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


Кто-нибудь знаком с этим дизайном пространства? Я склоняюсь ко второй фразеологии, хотя на практике это выглядит необычно. Но есть, вероятно, входы и выходы, известные тем, кто ходил сюда раньше.

2

Решение

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

Альтернативы 1 и 2 могут быть объединены в лучший из двух миров, который

  • всегда позволяет оптимальное разделение проблем (путем разделения класса на черты и реализацию)
  • не требовать расщепление класса
  • никогда не требует открытия пространства имен библиотеки

Дополнительный кусок клея необходим в форме мета-функции на основе ADL, отображающей любой класс на его характеристики.

template< typename t >
t traits_type_entry( t const & ); // Declared, never defined.

template< typename t >
using traits_type = decltype( traits_type_entry( std::declval< t >() ) );

По умолчанию, T служит свой собственный тип черт как traits_type< T >::type является T, Чтобы изменить это для данного типа t к классу черт t_traits, объявить (но не определить) функцию t_traits traits_type_entry( t const & ), это t_traits класс может или не может быть базовым классом t; traits_type объекту все равно. Поскольку функция будет найдена путем поиска аргумента-зависимости, она может быть объявлена ​​как функция друга без объявления в области имен.

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

class outer_scope {
struct special;
struct special_traits {
typedef int value_type;
constexpr static int limit = 5;
};
friend special_traits traits_type_entry( special const & );

struct unspecial {
typedef double baz_type;
int table[ util::traits_type< special >::limit ];
};

struct special : special_traits {
void f() {
std::pair< typename util::traits_type< unspecial >::baz_type,
value_type >();
}
};
};

http://ideone.com/QztQ6i

Обратите внимание t const & параметр для traits_type_entry может быть просто t пока класс является копируемым и разрушаемым.

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

1

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

Других решений пока нет …

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