Можно ли использовать приведение в стиле C для встроенных типов? - PullRequest
17 голосов
/ 09 февраля 2009

Прочитав здесь много ответов о кастинге в стиле C в C ++, у меня остался один маленький вопрос. Могу ли я использовать приведение в стиле C для встроенных типов, таких как long x=(long)y;, или оно все еще считается плохим и опасным?

Ответы [ 9 ]

28 голосов
/ 09 февраля 2009

Можно ли использовать приведение в стиле C для встроенных типов, таких как long x = (long) y; или это все еще считается плохим и опасным?

Не используйте их, никогда. Причины их использования применимы и здесь. По сути, как только вы их используете, все ставки отключены, потому что компилятор вам больше не поможет. Хотя это более опасно для указателей, чем для других типов, оно потенциально все еще опасно и дает плохую диагностику компилятора в случае ошибок, тогда как приведения новых стилей предлагают более богатые сообщения об ошибках, поскольку их использование более ограничено: Мейерс цитирует пример отбрасывания const ness: использование любого приведения, отличного от const_cast, не будет компилироваться, таким образом проясняя , что происходит здесь.

Кроме того, некоторые другие недостатки применяются независимо от типов, а именно синтаксические соображения: приведение в стиле C очень ненавязчиво. Это нехорошо: приведения C ++ четко выделяются в коде и указывают на потенциально опасный код. Их также можно легко найти в IDE и текстовых редакторах. Попробуйте найти приведение в стиле C в большом коде, и вы увидите, насколько это сложно.

С другой стороны, приведения в стиле C не дают никаких преимуществ по сравнению с приведениями в C ++, поэтому даже не стоит искать компромисс.

В более общем плане Скотт Мейерс советует «Минимизировать приведение» в Effective C ++ (пункт 27), поскольку «приведение приводит к разрушению системы типов».

17 голосов
/ 09 февраля 2009

Я бы не стал, по следующим причинам:

  • Приведения являются уродливыми, должны быть уродливыми и выделяться в вашем коде, и их можно найти с помощью grep и аналогичных инструментов.
  • «Всегда использовать приведение C ++» - это простое правило, которое с гораздо большей вероятностью будет запомнено и соблюдено, чем «Использование приведений C ++ для пользовательских типов, но можно использовать приведение в стиле C для встроенных типов. «
  • Приведения в стиле C ++ предоставляют другим разработчикам больше информации о том, почему приведение необходимо.
  • Приведения в C-стиле могут позволить вам делать преобразования, которые вы не намеревались - если у вас есть интерфейс, который принимает (int *), и вы использовали приведения в C-стиле, чтобы передать ему const int *, и интерфейс если принять длинное *, ваш код, использующий приведения в стиле c, продолжит работать, даже если это не то, что вы хотели.
8 голосов
/ 07 сентября 2012

Если вы преобразуете из числового типа, в другого числового типа, то я думаю, что приведения в стиле C * предпочтительны в *_cast. У каждого из операторов *_cast есть особая причина не использовать его для числовых типов:

  • reinterpret_cast, когда применяется к числовым типам, выполняет обычное числовое преобразование, а не реинтерпретирует биты, т. Е. Запись reinterpret_cast<uint64_t>(3.14159) не дает целое число с тем же битовым представлением, что и с плавающей запятой постоянная. Это противоречит интуиции.
  • const_cast никогда не требуется для числового значения и, если применяется к числовой переменной (в отличие от указателя или ссылки на число), предполагает, что тип этой переменной неверно.
  • dynamic_cast просто не имеет смысла для чисел, потому что числа никогда не имеют динамического типа.
  • static_cast обычно используется в типах классов. Поэтому выглядит странно , чтобы применить static_cast к числу; Ваши читатели поцарапают себе голову и зададутся вопросом, есть ли что-то, что они не знают о static_cast, что делает его отличным от броска в стиле C применительно к числу. (На самом деле, они идентичны, но мне пришлось пару раз прочитать соответствующий раздел спецификаций C ++, чтобы быть уверенным , что они были идентичны, и у меня все еще есть реакция «что-то странное, должно быть, происходит здесь»). )

и есть дополнительная стилистическая причина, чтобы их избегать:

  • Если вам нужно привести число к числовым типам, вам, вероятно, потребуется выполнить несколько из них в кластере; из-за этого важна краткость бросков в стиле C. Например, программа, которую я сейчас пишу, имеет целый ряд подобных вещей:

    uint32_t n = ((uint32_t(buf[0]) << 24) |
                  (uint32_t(buf[1]) << 16) |
                  (uint32_t(buf[2]) <<  8) |
                  (uint32_t(buf[3])      ));
    

    Использование static_cast здесь может затмить арифметику, которая является важной вещью. (Примечание: эти приведения не нужны, только если sizeof(uint32_t) <= sizeof(unsigned int), что является безопасным предположением в настоящее время, но я все еще предпочитаю явность.) (Да, я, вероятно, должен выделить эту операцию в be32_to_cpu inline помощник, но я бы все так же кодировал.)

3 голосов
/ 08 июля 2009

Я могу вспомнить одно законное использование для броска в стиле C:

// cast away return value to shut up pedantic compiler warnings
(void)printf("foo\n");
2 голосов
/ 30 июня 2010

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

В компании, в которой я работаю, мы компилируем с максимальным (уровень 4) предупреждениями, поэтому мы получаем предупреждения о каждом небольшом приведении типа и т. Д ... Поэтому в них я использую приведение в стиле c, потому что они маленькие, не такие многословные и имеют смысл.

for (int i = 0; i < (int)myvec.size(); i++)
{
  // do something int-related with i
}
float val = (float)atof(input_string);

и т.д ....

Но если включенный (например, библиотечный) код, который может измениться, то static_cast <> () лучше, потому что вы можете гарантировать, что компилятор выдаст ошибку, если типы изменятся и приведение больше не имеет смысла. Кроме того, невозможно найти приведения в коде, если вы используете только c-стиль. «static_cast<mytype>(» довольно легко найти. :)

2 голосов
/ 14 февраля 2009

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

/* Convert -1..1 to -32768..32767 */
short float_to_short(float f)
{
    return (short)(max(-32768.f, min(32767.f, f * 32767.f)));
}
0 голосов
/ 11 марта 2013

Единственный случай, когда я предпочитаю устаревшее приведение C, это приведение байтовых буферов к разным знакам. Многие API имеют разные соглашения, и на самом деле нет «правильного ответа», и приведение не опасно в том контексте, в котором оно выполняется, когда код должен быть настолько независимым от платформы, как комбинация используемых библиотек.

Конкретный пример того, что я имею в виду, я думаю, это просто прекрасно:

unsigned char foo[16];
lib1_load_foo(key);
lib2_use_foo((char*)key);

Но для всего остального, если требуется приведение, у него будут потенциальные побочные эффекты, которые должны выделяться, и использование броска в стиле C ++ (возможно, некрасивое) является правильным выбором. А если приведение не требуется, не используйте приведение.

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

Зачем вам нужен именно этот актерский состав? Любой числовой тип может быть преобразован в long без приведения (при потенциальной потере точности), поэтому приведение не позволяет компилятору делать то, что он уже не может. Все, что вам нужно сделать, это исключить возможность компилятора предупреждать о потенциальной проблеме. Если вы конвертируете какой-то другой базовый тип (например, указатель) в long, мне бы очень хотелось увидеть reinterpret_cast <>, а не приведение типа C, чтобы я мог легко найти, что происходит, если получится быть проблемой.

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

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

Если вы обнаружите, что вам нужно выполнить приведение в стиле C (или reinterpret_cast), то внимательно посмотрите на свой код, и он на 99,99% уверен, что с ним что-то не так. Оба этих приведения почти неизбежно привели к специфическому (и очень часто неопределенному) поведению при реализации.

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