Внутренние typedefs в С++ - хороший стиль или плохой стиль?

Что-то, что я часто делал в последнее время, это объявление typedefs, относящихся к определенному классу внутри этого класса, т.е.

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) на более позднем этапе, это будет иметь минимальное влияние на код.
  • Я думаю, что он выглядит "красивее" и, возможно, легче читать.

Причины, по которым мне это не нравится:

  • Иногда возникают проблемы с зависимостями - если вы хотите вставить, скажем, Lorem::vector в другой класс, но нужно (или хотите) перенаправить объявление Lorem (в отличие от введения зависимости от его заголовочного файла), тогда вы в конечном итоге придется использовать явные типы (например, boost::shared_ptr<Lorem>, а не Lorem::ptr), что немного непоследовательно.
  • Это может быть не очень распространено и, следовательно, сложнее понять?

Я стараюсь быть объективным с моим стилем кодирования, поэтому было бы неплохо получить некоторые другие мнения по этому поводу, чтобы немного осмыслить свое мышление.

Ответ 1

Я думаю, что это отличный стиль, и я сам его использую. Лучше всего максимально ограничить область имен, а использование классов - лучший способ сделать это на С++. Например, в стандартной библиотеке С++ широко используется typedefs внутри классов.

Ответ 2

Он служит выражением о намерениях - в приведенном выше примере класс Lorem предназначен для подсчета ссылок через boost:: shared_ptr и хранятся в вектор.

Это именно то, чего он не делает.

Если я вижу "Foo:: Ptr" в коде, я абсолютно не знаю, является ли это shared_ptr или Foo * (STL имеет:: typedefs указателя, которые являются T *, помните) или что-то еще. Особенно если это общий указатель, я вообще не предоставляю typedef, но сохраняю использование shared_ptr явно в коде.

На самом деле, я почти никогда не использую typedefs вне Template Metaprogramming.

STL делает этот тип вещей все время

Дизайн STL с понятиями, определенными в терминах функций-членов и вложенных typedefs, является историческим тупиком, современные библиотеки шаблонов используют классы свободных функций и признаков (см. Boost.Graph), поскольку они не исключают встроенные шаблоны, в типах моделирования модели и потому, что упрощает адаптацию типов, которые не были разработаны с учетом понятийных библиотек шаблонов.

Не используйте STL в качестве причины для совершения тех же ошибок.

Ответ 4

Typdefs - это, безусловно, хороший стиль. И все ваши "причины, которые мне нравятся" хороши и правильны.

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

Вы можете перемещать typedef вне класса, но Class:: ptr так красивее, чем ClassPtr, что я этого не делаю. Это похоже на пространства имен, как и для меня, - вещи остаются связанными в рамках области.

Иногда я делал

Trait<Loren>::ptr
Trait<Loren>::collection
Trait<Loren>::map

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

Ответ 5

STL делает это все время - typedefs являются частью интерфейса для многих классов в STL.

reference
iterator
size_type
value_type
etc...

- все typedefs, которые являются частью интерфейса для различных классов шаблонов STL.

Ответ 6

Еще один голос за то, что это хорошая идея. Я начал делать это, написав симуляцию, которая должна была быть эффективной, как во времени, так и в пространстве. Все типы значений имели Ptr typedef, который начинался как общий указатель с повышением. Затем я сделал некоторое профилирование и изменил некоторые из них на ускорительный интрузивный указатель без необходимости изменять какой-либо код, в котором эти объекты использовались.

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

Ответ 7

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

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

Ответ 8

В настоящее время я работаю над кодом, который интенсивно использует эти типы typedef. Пока все в порядке.

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

Поэтому, прежде чем использовать их, необходимо рассмотреть некоторые моменты

  • Кто-нибудь еще нуждается в этих typedefs? Класс много используется другими классами?
  • Я сокращаю использование или скрываю класс? (В случае укрытия вы также можете думать об интерфейсах.)
  • Другие люди работают с кодом? Как они это делают? Считают ли они, что это легче или они будут запутаны?

Ответ 9

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