Можно ли использовать C-стиль для встроенных типов?

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

Ответ 1

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

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

Ответ 2

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

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

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

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

В более общем плане Скотт Мейерс советует "Свести к минимуму касты" в "Эффективном С++" (пункт 27), потому что "бросает подрывную систему типов".

Ответ 3

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

  • reinterpret_cast, когда применяется к числовым типам, выполняется обычное числовое преобразование, а не реинтерпретация битов, т.е. запись reinterpret_cast<uint64_t>(3.14159) не создает целое число с таким же представлением бит, как и константа с плавающей запятой. Это противоречит интуиции.
  • const_cast никогда не требуется для числового значения, и если применяется к числовой переменной (в отличие от указателя или ссылки на число), предполагает, что тип этой переменной неверен.
  • dynamic_cast просто нет смысла для чисел, потому что числа никогда не имеют динамического типа.
  • static_cast обычно используется для типов классов. Поэтому странно применять static_cast к числу; ваши читатели поцарапают свои головы и задаются вопросом, есть ли что-то, что они не знают о static_cast, что отличает его от приведения стиля 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, но я бы все еще кодируйте его таким же образом.)

Ответ 4

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

// cast away return value to shut up pedantic compiler warnings
(void)printf("foo\n");

Ответ 5

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

/* Convert -1..1 to -32768..32767 */
short float_to_short(float f)
{
    return (short)(max(-32768.f, min(32767.f, f * 32767.f)));
}

Ответ 6

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

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

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>(" довольно легко найти.:)

Ответ 7

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

Ответ 8

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

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

Ответ 9

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

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

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

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