Внутренние 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 в качестве причины для совершения тех же ошибок.
Ответ 3
Typedef - это те, что основанный на политике дизайн и черты, построенные на С++, поэтому Сила общего программирования на С++ связана с самими typedef.
Ответ 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 должен быть известен вне класса. В этом случае я рекомендую перемещать их вне класса.