Что-то, что я часто делал в последнее время, - это объявление typedef, относящихся к определенному классу внутри этого класса, т.е.
class Lorem
{
typedef boost::shared_ptr<Lorem> ptr;
typedef std::vector<Lorem::ptr> vector;
//
// ...
//
};
Эти типы затем используются в другом месте кода:
Lorem::vector lorems;
Lorem::ptr lorem( new Lorem() );
lorems.push_back( lorem );
Причины, по которым мне это нравится:
- Это уменьшает шум, создаваемый шаблонами классов,
std::vector<Lorem>
становитсяLorem::vector
и т. Д. - Он служит заявлением о намерениях - в приведенном выше примере класс Lorem предназначен для подсчета ссылок
boost::shared_ptr
и сохранения в векторе. - Это позволяет изменить реализацию - то есть, если Lorem нужно было изменить для навязчивого подсчета ссылок (через
boost::intrusive_ptr
) на более позднем этапе, то это окажет минимальное влияние на код. - Я думаю, что он выглядит «красивее» и, возможно, его легче читать.
Причины, по которым мне это не нравится:
- Иногда возникают проблемы с зависимостями - если вы хотите встроить, скажем, a
Lorem::vector
в другой класс, но вам нужно (или вы хотите) только переслать объявление Lorem (в отличие от введения зависимости от его файла заголовка), тогда вам придется использовать явные типы (например,boost::shared_ptr<Lorem>
а неLorem::ptr
), что немного противоречиво. - Это может быть не очень распространено и, следовательно, труднее понять?
Я стараюсь быть объективным со своим стилем программирования, поэтому было бы неплохо узнать о нем другие мнения, чтобы я мог немного проанализировать свое мышление.
c++
coding-style
typedef
Уилл Бейкер
источник
источник
Это именно то, чего он не делает.
Если я вижу в коде «Foo :: Ptr», я совершенно не понимаю, является ли это shared_ptr или Foo * (в STL есть определения типов :: pointer typedef, которые являются T *, помните) или что-то еще. Esp. если это общий указатель, я вообще не предоставляю typedef, но сохраняю использование shared_ptr явно в коде.
На самом деле, я почти никогда не использую typedef вне метапрограммирования шаблонов.
Дизайн STL с концепциями, определенными в терминах функций-членов и вложенных определений типов, является историческим тупиком, современные библиотеки шаблонов используют бесплатные функции и классы свойств (см. Boost.Graph), поскольку они не исключают встроенные типы из моделирование концепции и потому, что это упрощает адаптацию типов, которые не были разработаны с учетом концепций заданных библиотек шаблонов.
Не используйте STL как повод для тех же ошибок.
источник
std::allocator_traits<Alloc>
класс ... вам не нужно специализировать его для каждого отдельного распределителя, который вы пишете, потому что он просто заимствует типы непосредственно изAlloc
.Alloc
автору специализироватьсяstd::allocator_traits<>
на новом типе не сложнее, чем добавить необходимые определения типов. Я также отредактировал ответ, потому что мой полный ответ не вписался в комментарий.allocator_traits
попытке создать пользовательский аллокатор, я не беспокоить с пятнадцатью членами класса черты ... все , что нужно сделать , это сказать ,typedef Blah value_type;
и предоставить соответствующие функции - членов, и по умолчаниюallocator_traits
будет выяснить отдых. Далее посмотрите на ваш пример Boost.Graph. Да, он интенсивно использует класс признаков ... но по умолчанию реализация простыхgraph_traits<G>
запросовG
для собственных внутренних определений типов.iterator_traits
класс, чтобы ваши общие алгоритмы могли легко запрашивать соответствующую информацию. Что, опять же, по умолчанию запрашивает у итератора собственную информацию. Короче говоря, черты и внутренние определения типов вряд ли являются взаимоисключающими ... они поддерживают друг друга.iterator_traits
стало необходимым, потому что онT*
должен быть модельюRandomAccessIterator
, но вы не можете поместить в него требуемые typedefT*
. Как только мы это сделалиiterator_traits
, вложенные определения типов стали излишними, и я бы хотел, чтобы их тут же удалили. По той же причине (невозможность добавления внутренних определений типов)T[N]
не моделируетSequence
концепцию STL , и вам понадобятся такие кладжи, какstd::array<T,N>
. Boost.Range показывает, как можно определить современную концепцию Sequence, котораяT[N]
может моделировать, поскольку для нее не требуются ни вложенные определения типов, ни функции-члены.Typedef - это то, на чем основан дизайн на основе политик и черты, построенные на C ++, поэтому сила универсального программирования в C ++ проистекает из самих typedef.
источник
Типы определенно хороший стиль. И все ваши «причины, которые мне нравятся» хороши и правильны.
О проблемах, которые у вас есть с этим. Что ж, предварительное заявление - это не Святой Грааль. Вы можете просто разработать свой код, чтобы избежать многоуровневых зависимостей.
Вы можете переместить typedef за пределы класса, но Class :: ptr намного красивее, чем ClassPtr, что я этого не делаю. Это похоже на пространства имен, как для меня - вещи остаются связанными в рамках.
Иногда я делал
Trait<Loren>::ptr Trait<Loren>::collection Trait<Loren>::map
И он может быть по умолчанию для всех классов домена и с некоторой специализацией для некоторых.
источник
STL постоянно делает такие вещи - определения типов являются частью интерфейса для многих классов в STL.
все это typedef, которые являются частью интерфейса для различных классов шаблонов STL.
источник
Еще одно голосование за то, что это хорошая идея. Я начал это делать, когда писал симуляцию, которая должна была быть эффективной как во времени, так и в пространстве. Все типы значений имели Ptr typedef, который начинался как повышающий общий указатель. Затем я провел некоторое профилирование и изменил некоторые из них на назначаемый указатель Boost без необходимости изменять какой-либо код, в котором использовались эти объекты.
Обратите внимание, что это работает только тогда, когда вы знаете, где будут использоваться классы, и что все виды использования имеют одинаковые требования. Я бы не стал использовать это, например, в коде библиотеки, потому что при написании библиотеки вы не можете знать контекст, в котором она будет использоваться.
источник
В настоящее время я работаю над кодом, который интенсивно использует такие определения типов. Пока все в порядке.
Но я заметил, что довольно часто встречаются итеративные определения типов, определения разделены между несколькими классами, и вы никогда не знаете, с каким типом имеете дело. Моя задача - суммировать размер некоторых сложных структур данных, скрытых за этими определениями типов, поэтому я не могу полагаться на существующие интерфейсы. В сочетании с тремя-шестью уровнями вложенных пространств имен, это сбивает с толку.
Поэтому перед их использованием следует учесть некоторые моменты.
источник
Я рекомендую переместить эти typedef за пределы класса. Таким образом, вы удаляете прямую зависимость от общих указателей и векторных классов и можете включать их только при необходимости. Если вы не используете эти типы в реализации своего класса, я считаю, что они не должны быть внутренними определениями типов.
Причины, по которым вам это нравится, по-прежнему совпадают, поскольку они решаются с помощью псевдонима типов через typedef, а не путем объявления их внутри вашего класса.
источник
Когда typedef используется только внутри самого класса (т.е. объявлен как закрытый), я думаю, что это хорошая идея. Однако по точно указанным вами причинам я бы не стал использовать его, если typedef необходимо знать за пределами класса. В таком случае рекомендую вынести их за пределы класса.
источник