printУлучшения языка C

printКонстанты и замена для препроцессора

В языке С квалификатор const используется только для указания, что объект нельзя изменять, например, параметр не меняется внутри функции:
char *strcpy(char *dst, const char *src);

Именованные константы в языке C определяются либо с помощью команды препроцессора #define (в этом случае нельзя будет определить локально переменную с тем же именем), либо с помощью enum (только для целых констант).
В С++ можно создавать типизированные именованные константы с ограниченной областью видимости с помощью квалификатора constexpr. Квалификатор const также можно использовать для определения констант, но чаще нужен для указания, что значение объекта не должно меняться.

constexpr int size=100;
int a[size];
constexpr double pi=3.14159265358979323846;

Появилась специальная константа nullptr, которая которая неявно может преобразована в указатель любого типа, но не в число. В языке С для определения нулевого указателя NULL использовалась константа 0, что могло привести к путанице в C++.

Вместо константы I языка С для определения комплексных чисел (12.5+1.5e8*I) в C++ введен
суффикс i: 12.5+1.5e8i. Кроме того введены суффиксы для промежутков времени: 2h, 15min, 9s, 11ms, 50us, 2ns. Программист может добавить свои суффиксы для констант.

Для записи двоичных чисел используется префикс 0b (0b1010), разряды в длинных числах можно разделять апострофами (12'345'671).

С помощью enum class можно перечислить константы, которые являются типобезопасными и требуют явного преобразования в int и обратно. Также для перечислений можно указать базовый целый тип (по умолчанию int).
void f(int);
enum Switch: char {on, off};
enum class Color { green, red, blue, white };
Switch s1=off;
Color c1=Color::red;
f(s1); // OK
f(c1); // Error
f(int(c1)); // OK

Если перед функцией указать constexpr, то при вызове такой функции с аргументами-константами, её значение будет вычислено во время компиляции.

constexpr int gcd(int a, int b){
  return b ? gcd(b, a % b) : a;
}
int main()
{ int x=gcd(110,33); // x=11;
  int a=110,b=33;
  x=gcd(a,b); // обычный вызов
}

Функция, определенная с помощью спецификатора inline становится встраиваемой, т.е. её код подставляется в точку вызова. При этом устраняются все недостатки, свойственные макроопределениям. Время работы программы уменьшается за счет операций сохранения/восстановления регистров и передачи управления, но размер программы увеличивается. Спецификатор inline носит рекомендательный характер, для больших функций компилятор генерирует обычный вызов. Тело встраиваемой функции определяется в заголовочном файле.
inline int max(int x, int y)
{ return x>y?x:y;
}
Макроопределение #define max(x,y) ((x)>(y)?(x):(y)) работает для любых типов данных. Чтобы функция max работала так же, её нужно определить как шаблон:
template <typename T>
inline T max(T x, T y)
{ return x>y?x:y;
}
Для условной компиляции вместо #ifdef/#else/#endif во многих случаях можно использовать константы и условный оператор с квалификатором constexpr. Компилятор не будет генерировать код для ложных условий.
constexpr bool DEBUG=false;
...
if constexpr(DEBUG)
{ cout<<"x="<<x<<endl;
}

Вместо комбинации команд препроцессора #if...#endif и #error, была добавлена возможность выполнять необходимые проверки компилятору:
static_assert(sizeof(int)!=sizeof(void *), "Cannot convert pointer to int.");

Таким образом препроцессор в C++ используется в основном для подключения заголовочных файлов, но в C++20 предложены модули как замена и для этих команд.
export module sample;
export {
  constexpr int n=42;
  int sqr(int x) { return x*x; }
}
Для подключения этого модуля и заголовочных файлов STL выглядит так:
import sample;
import <iostream>;
int main() {
  cout<<sqr(n)<<'\n';
}
В MinGW C++ нужна предварительная компиляция заголовочных файлов, результаты которой сохраняются в папке проекта:
g++ -std=c++23 -c -fmodule-header=system -x c++-system-header iostream ...
В С++23 для подключения всех стандартных заголовочных файлов C++ и C можно написать:
import std;

Также в C++20 появились спецификаторы consteval для функций, проверяющий, что функция вызывается только с константными аргументами (constexpr-функция может вызываться не только для аргументов-констант) и constinit для переменных, который проверяет, что переменная инициализируется константой. В C++23 добавлен оператор ветвления для constexpr-функций:

constexpr int fun(int n)
  if consteval {
    // код для вызова с аргументами-константами
  }
  else {
    // код для вызова с обычными аргументами
  }
}
loading