Когда имеет смысл вводить определения основных типов данных? - PullRequest
6 голосов
/ 09 февраля 2011

Внутренний документ стандартов кодирования c ++ компании гласит, что даже для базовых типов данных, таких как int, char и т. Д., Следует определять собственные определения типов, такие как "typedef int Int". Это оправдано преимуществом переносимости кода. Однако существуют ли общие соображения / советы относительно того, когда (в отношении каких типов проектов) это действительно имеет смысл? Заранее спасибо ..

Ответы [ 7 ]

8 голосов
/ 09 февраля 2011

Typedefing int до Int практически не дает никаких преимуществ (это не дает никакой семантической выгоды и приводит к абсурдам, таким как typedef long Int на других платформах, чтобы оставаться совместимыми).

Однако typedefing *От 1006 * до, например, int32_t (вместе с long до int64_t и т. Д.) Действительно дает преимущество, поскольку теперь вы можете свободно выбирать тип данных с соответствующей шириной в режиме самодокументирования, и он будет переносимым (просто переключите typedef на другой платформе).

Фактически, большинство компиляторов предлагают stdint.h, который уже содержит все эти определения.

3 голосов
/ 09 февраля 2011

typedef int Int - это ужасная идея ... люди будут удивляться, смотрят ли они на C ++, это сложно набирать, визуально отвлекает, и единственное смутное объяснение для этого является ошибочным, но давайте поместим это явно, чтобы мы могли сбить его с ног:

если в один прекрасный день говорят, что 32-битное приложение портировано на 64-битное, и существует множество глупых кодов, которые работают только для 32-битных целых, то по крайней мере typedef можно изменить на держать Int на 32 бита.

Критика: если система засорена тем кодом, который написан так плохо (т.е. не использует явно 32-битный тип из cstdint), в подавляющем большинстве случаев могут быть другие части кода, где теперь нужно будет использовать 64- Битовые биты, которые застрянут в 32-битной версии через typedef. Код, взаимодействующий с библиотечными / системными API-интерфейсами с использованием целочисленных значений, скорее всего получит целочисленные значения, что приведет к усеченным дескрипторам, работающим до тех пор, пока они не окажутся за пределами 32-битного диапазона и т. Д. Код должен быть полностью пересмотрен, прежде чем в любом случае будет заслуживающим доверия. Наличие такого обоснования в сознании людей может только отговорить их от использования типов явно заданного размера, где они действительно полезны («для чего вы это делаете?», «Переносимость?», «Но Int для переносимости, просто используйте это»).

Тем не менее, правила кодирования могут быть предназначены для поощрения typedef для вещей, которые являются логически различными типами, такими как температура, цены, скорости, расстояния и т. Д. В этом случае typedefs могут быть весьма полезны, поскольку они позволяют легко способ перекомпилировать программу, скажем, обновить с float точность до double, понизить с реального типа на цельный или заменить пользовательский тип некоторыми специальными поведениями. Это также очень удобно для контейнеров, так что при изменении контейнера будет меньше работы и меньше влияния на клиента, хотя такие изменения, как правило, немного болезненны: API-интерфейсы контейнера разработаны так, чтобы быть немного несовместимыми, поэтому важные части должны быть пересмотрены вместо того, чтобы компилировать, но не работать или молча выполнять намного хуже, чем раньше.

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

template <typename T, int N>
struct Distinct
{
    Distinct(const T& t) : t_(t) { }
    operator T&() { return t_; }
    operator const T&() const { return t_; }
    T t_;
};

typedef Distinct<float, 42> Speed;

Но трудно сделать значения N уникальными ... возможно, вы можете иметь центральный enum, перечисляющий различные значения, или использовать __LINE__, если вы имеете дело с одной единицей перевода и без нескольких typedefs на линии, или возьмите const char* из __FILE__, но я не знаю особо элегантного решения.

(Одна классическая статья 10 или 15 лет назад продемонстрировала, как можно создавать шаблоны для типов, которые знают несколько ортогональных единиц, сохраняя счетчики текущей «мощности» в каждой и настраивая тип по мере выполнения умножений, делений и т. Д. Например, вы можете объявить что-то вроде Meters m; Time t; Acceleration a = m / t / t; и проверить, все ли единицы были разумны во время компиляции.)

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

3 голосов
/ 09 февраля 2011

Typedefs может помочь в описании семантики типа данных.Например, если вы typedef float distance_t;, вы даете разработчику понять, как будут интерпретироваться значения distance_t.Например, вы можете сказать, что значения никогда не могут быть отрицательными.Что такое -1,23 километра?В этом сценарии это может просто не иметь смысла с отрицательными расстояниями.

Конечно, typedefs никоим образом не ограничивает область значений.Это просто способ сделать код (по крайней мере) читабельным и передать дополнительную информацию.

Проблемы переносимости, о которых, по-видимому, упоминается на рабочем месте, могут возникнуть, когда вы хотите убедиться, что конкретный тип данных всегда одинаков.размер, независимо от того, какой компилятор используется.Например

#ifdef TURBO_C_COMPILER
typedef long int32;
#elsif MSVC_32_BIT_COMPILER
typedef int int32;
#elsif
...
#endif
3 голосов
/ 09 февраля 2011

Мусор.

«Переносимость» не имеет смысла, потому что int всегда означает int.Если они думают, что хотят что-то вроде целочисленного типа, который является 32-битным, тогда typedef должен быть typedef int int32_t;, потому что тогда вы называете реальный инвариант и можете фактически гарантировать, что этот инвариант выполняется, через препроцессор и т. Д.

Но это, конечно, пустая трата времени, потому что вы можете использовать <cstdint>, либо в C ++ 0x, либо в виде расширений, либо использовать его реализацию Boost в любом случае.

3 голосов
/ 09 февраля 2011

Это зависит.Пример, который вы приводите:

typedef int Int;

просто тупой.Это немного похоже на определение константы:

const int five = 5;

Точно так же, как нулевая вероятность того, что переменная five когда-либо станет другим числом, typedef Int может ссылаться только на примитивный тип int.

OTOH, typedef, подобный этому:

typedef unsigned char byte;

облегчает жизнь пальцам (хотя он не имеет преимуществ в мобильности), и такой как:

typedef unsigned long long uint64;

И набрать текст легче, и переносимее, поскольку в Windows вместо этого я бы написал: (101)

typedef unsigned __int64 uint64;
0 голосов
/ 30 декабря 2011

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

0 голосов
/ 09 февраля 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...