Holy C++

В этой статье постараюсь затронуть все вещи, которые можно без зазрения совести выкинуть из С++ не потеряв ничего (кроме боли), уменьшить стандарт, нагрузку на создателей компиляторов, студентов изучающих язык и мемосоздавательный потенциал громадности С++

В первую очередь хочется убрать из языка то, что приводит к частым ошибкам и мешает развитию языка, тут идеальным кандидатом можно назвать

1 — union — сумм тип из 70х, в С идея хранения одного типа из нескольких в одном участке памяти выглядит неплохо и сейчас, ведь там все типы это набор байт с заданным размером.

В С++ же использование union это автоматическое undefined behavior, например:

#include 
union A { int x; float y;};
union B {
  B() {} // требуется написать какой то конструктор и деструктор
  // но обратите внимание, что написать деструктор правильно невозможно
  // (попробуйте, если не верите)
  ~B() {}
  std::string s;
  int x;
};
int main() {
  A value;
  value.x = 5;
  value.y; // undefined behavior, обращение к неактивному члену union
  B value2;
  value2.s = "hello world";
  // undefined behavior, поле s неактивно и используется 
  // (в операторе= для std::string)
}

Как вы видите использовать union без ошибок просто невозможно, при этом вам постоянно придётся вручную вызывать правильный деструктор для объекта и вместо приравнивания делать placement new в нужное поле. Так зачем же так мучаться, если можно сделать нормальный тип с хорошим интерфейсом БЕЗ какого либо оверхеда относительно юниона?

Следующий код полностью заменяет юнион, не имеет никакого оверхеда относительно него и имеет более понятный пользователю интерфейс (emplace / destroy)

Смотреть только если знаете С++

template
struct union_t {
  alignas(std::ranges::max({alignof(Ts)...})
  std::byte data[std::ranges::max({sizeof(Ts)...});
  
  template U>
  constexpr U& emplace(auto&&... args) {
    return std::launder(new(data) U{std::forward(args)....});
  }
  template U>
  constexpr void destroy_as() const {
    reintepret_cast(reinterpret_cast(data))->~U();
  }
};

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

2 — массивы

Это может звучать странно, но мы правда можем убрать из С++ массивы не потеряв ничего (убрав этот чудовищный синтаксис char (&&…arr)[N] (угадайте в комментариях что это значит))

К тому же массивы почему то не копируемы и не умеют в мув семантику, что делает их самыми неполноценными типами во всём языке

Как же их заменить? Рекусивным (или через множественное наследование) туплом с элементами одного типа (да, это было очевидно)))

Интересный факт:

в тексте стандарта С++ есть исключение аж в цикле for для сишных массивов… Что подтверждает очевидное — массивы безумно плохо соотносятся с остальным языком

Реализация массива без массива

template
struct array_value { T value; };
template
struct array_impl;
template
struct array_impl> : array_value...{};

template
struct array_ : array_impl> {
  // тут какой-то интерфейс массива по вашему желанию
    T& operator[](size_t n) {
        // такая реализация для краткости
        return *(reinterpret_cast(reinterpret_cast(this)) + n);
    }
};

3 — тип void

void по большей части служит для того, чтобы делать под него исключения в обобщённом коде, было бы гораздо удобнее иметь тип с единственным ничего не значащим значением… Как же сделать такой тип…

struct [[maybe_unused]] nulltype {};
 // Вот и всё... Да и аттрибут [[maybe_unused]] тут разве что для красоты

4 — все фундаментальные типы…
Кажется мы идём по нарастающей, на что же автор статьи тут замахнулся? На int?!

Да, не удались в С фундаментальные типы, а С++ их унаследовал. Кто в здравом уме будет использовать int, который может занимать 8 байт, но гарантирует свои значения только до 2 ^ 16??? Это буквально создатель ошибок (особенно у новичков)

Заменить это всё можно одним фундаментальным типом byte и указателями, действительно: с помощью byte и системы типов С++ можно создавать любые типы, в том числе аналогичные int, double, float, bool и т.д. из фундаментального набора
Тут мы убиваем сразу несколько зайцев — нет больше исключений для фундаментальных типов в разрешении перегрузки, нет исключений в шаблонном коде для наследования (от фундаментальных типов нельзя наследоваться) ну и другие более мелкие исключения для подобных типов уходят в прошлое

4.5 — приведения типов из С — это.просто.не.должно.компилироваться. (но оно компилируется) https://godbolt.org/z/fz6eMEeqG

int main() {
    (void)(5), (void)5, void(5);
}

5 — runtime variadic arguments — человек, который придумал эту вещь в С должно быть сейчас раскаивается за этот грех, но нам приходится его тянуть.

И даже не смотря на то, что так реализован знаменитый printf (const char* pattern, …) <- кто не понял, многоточие это рантайм аргументы! Любые! Это выглядит самый большой костыль в истории программирования, а как этим пользоваться... Ух... макросы __VA_START__ __VA_COPY__ и громадная куча ещё всего связанного с этим будут сниться в кошмарах сишникам десятилетиями, а С++ пожалуй должен просто удалить этого демона из языка и забыть(и добавить за счёт удаления этого новые возможности пакам шаблонных аргументов)

6 — typedef — ну тут всё просто, в С++ есть отличная замена этому слову, просто сравните:

typedef void(*foo)(int); // foo теперь алиас на void(*)(int) (указатель на функцию)
// то же самое, но на С++
using foo = void(*)(int);

Смысла оставлять typedef в языке нет…))

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

Вот, кажется на этом этапе мы вычистили почти весь С из С++ и почти получили чистые ++(плюсы). Пора рассмотреть что стоит удалить тут!

8 — операторы new и delete

Действительно, зачем нужны в языке эти операторы, если всё давно перенесено на уровень абстракций аллокаторов, а память на низком уровне можно продолжать выделять через malloc?! Как вообще можно было догадаться внести систему (системный аллокатор памяти) на уровень языка?

Вы только посмотрите на даже не правила, а просто список перегрузок одного только new

СТРАШНО

48e666fb266693aa7ca53598c0458b63.png

Нужно только оставить размещающую версию оператора new для вызова конструктора по нужному адресу, всё остальное, особенно перегрузки new / delete использовать в современном С++ просто запрещено, если вы не хотите чтобы вас засмеяли

9 — ключевое слово class — ну тут я просто оставлю ссылку на мою же статью про бесполезность этого ключевого слова https://habr.com/ru/post/662351/

10 — ключевое слово final (запрет наследоваться от типа) — не имеет ни одного известного мне полезного применения, ломает обобщённый код, вердикт — удалить

11 — виртуальные методы :

Вызывают громадную кучу ошибок

Неэффективны, стимулируют писать архитектурно плохие решения, неэффективно использовать память, не позволяют использовать весь остальной язык, если используется ключевое слово virtual, и САМОЕ ГЛАВНОЕ — могут быть полностью заменены на другие языковые возможности без потери функционала (и с приобретением производительности, удобства, повторяемости кода, проверок на компиляции и т.д…)
Реализация динамического полиморфизма без виртуальных функций и их проблем: https://github.com/kelbon/AnyAny

12 — методы (указатель на текущий объект внутри реализации типа)

В С++23 появляется (наконец) deducing this, благодаря которому можно будет явно декларировать передачу this в методы типа, при этом такой «метод» будет фактически функцией (с точки зрения языка), а значит в последующем (вместе с удалением виртуальных методов) можно будет избавиться от самого понятия МЕТОД в языке С++(и указателя на эту вещь) (не дай боже вам перед сном увидеть декларацию указателя на метод)

struct A {
void foo(this A& self);
};

При этом возможно, что постепенно и ключевое слово this потеряет прежнее значение и останется только такое — декларация явной передачи ссылки/значения типа в функцию

Ну вот и всё, помечтали о великолепном hole C++, можете теперь пойти и опять продолжить писать хрень с виртуальным наследованием, забытым виртуальным деструктором на полиморфном типе и сишными кастами, удачи…

© Habrahabr.ru