﻿# Holy C\+\+

In this article, I'll try to list all things that we can throw out of C\+\+ without remorse\. This won't cost us anything but will reduce the standard \(and our headaches\), take the burden off the compiler developers and students who learn this language — and thus will eliminate the memetic potential of the C\+\+ enormity\.


> We published and translated this article with the copyright holder's permission\\\. The author is \[Kelbon\]\(https://habr\.com/en/users/Kelbon/\) \\\(\[kelbonage@gmail\\\.com\]\(mailto:kelbonage@gmail\.com\)\\\)\\\. The article was originally published on \[Habr\]\(https://habr\.com/en/post/672282/\)\\\.
> 
> This article may express some controversial points but we believe it is also a thought\\\-provoking piece\\\. Please note that this is a guest article\\\. The opinions expressed within the content are solely the author's and may not reflect the opinions and beliefs of the PVS\\\-Studio team\\\.
> 
> \[Subscribe to our newsletter\]\(https://pvs\-studio\.com/en/subscribe/\) so as not to miss the most interesting articles on our blog\\\.



![1010_Holy_Cpp/image1.png](https://import.viva64.com/docx/blog/1010_Holy_Cpp/image1.png)

## Unions

First of all, I want to remove the thing that often causes errors and pulls back the language development\. And by that I mean _union_, a sum type from the 70s\. In the C language, the idea of storing several types within a single memory fragment is still relevant — all types in C are a set of bytes with a fixed size\.

However, using _union_ in C\+\+ is an automatic undefined behavior\. Look:

```cpp
#include <string>
union A { int x; float y;};
union B {
  B() {} // you need to write a constructor and a destructor here
  // but it's impossible to write a destructor correctly
  // if you don't believe me, try to do it yourself
  ~B() {}
  std::string s;
  int x;
};
int main() {
  A value;
  value.x = 5;
  value.y; // undefined behavior, accessing the inactive union member 
  B value2;
  value2.s = "hello world";
  // undefined behavior, the s field is inactive and in use 
  // (operator = for std::string)
}
```

As you can see, it's impossible to use _union_ without errors\. Moreover, you have to manually call the correct destructor for an object all the time, and use the _placement new_ instead of the assigning operator in the desired field\. So why suffer when you can make a good type with a good interface WITHOUT any overhead relative to union?

The following code completely replaces union, doesn't have any overhead relative to it, and has a more user\-friendly interface \(_emplace_ / _destroy_\)\.

<details>
   <summary>Look only if you know C\\\+\\\+</summary>

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


</details>


Besides, the standard has a colossal number of exceptions for _union_, useless rules and restrictions\. And this thing can be abandoned and forgotten\. It's 2022, for God's sake\.\.\.

![1010_Holy_Cpp/image2.png](https://import.viva64.com/docx/blog/1010_Holy_Cpp/image2.png)

## Arrays

This may sound weird, but we can remove arrays from C\+\+ and lose nothing\. We can get rid of this awful syntax — _char\(&&\.\.\.arr\)\[N\]_ \(take a guess in comments what that means\)\.

In addition, arrays can't be copied and can't do move semantics, which makes them the most inferior types in C\+\+\.

How can they be replaced? With a recursive/"multiple inheritance based" tuple with elements of the same type \(this was obvious\)\.

**Fun fact**: The C\+\+ standard has an exception even for the _for_ loop with C arrays\.\.\. Which confirms the obvious — arrays poorly correlate with the rest of the language\.

<details>
   <summary>How to implement an array without an array?</summary>

```cpp
template<typename T, size_t I>
struct array_value { T value; };
template<typename, typename>
struct array_impl;
template<typename T, size_t... Is>
struct array_impl<T, std::index_sequence<Is...>> : array_value<T, Is>...{};

template<typename T, size_t N>
struct array_ : array_impl<T, std::make_index_sequence<N>> {
  // some array interface at your wish
    T& operator[](size_t n) {
        // such an implementation here is for short
        return *(reinterpret_cast<T*>(reinterpret_cast<void*>(this)) + n);
    }
};
```


</details>


## The void type

_void_ mostly serves for making exceptions for it in generalized code\. It would be more convenient to have a type with a single meaningless value\. Oh, if only there were a way to do it\.\.\.

```cpp
struct [[maybe_unused]] nulltype {};
 // That's it. Even the [[maybe_unused]] attribute is here purely for beauty
```

## All fundamental types\.\.\.

Seems like the stakes are getting higher, what does the author want to remove this time? _int_?\!

Yep, C had bad types and C\+\+ inherited them\. Who on earth would use _int_ that may take up to 8 bytes but guarantees its values only up to 2 ^ 16??? This is literally an error maker \(especially for junior developers\)\.

You can replace it with the _byte_ fundamental type and pointers\. Indeed: with _byte_ and the C\+\+ type system you can create any types even similar to _int_, _double_, _float_, _bool_, etc\., from fundamental set\.

Here we kill several birds with one stone — no more exceptions for fundamental types in overload resolution and no more exceptions in boilerplate code for inheritance \(you can't inherit from fundamental types\)\. Besides all small exceptions for such types become a thing of the past\.

## Conversions from C

This\. Shouldn't\. Compile\. At\. All\. \(But it does\): [https://godbolt\.org/z/fz6eMEeqG](https://godbolt.org/z/fz6eMEeqG)

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

## Runtime variadic arguments

The person who came up with this thing in C raised holy hell and now we have to live in it\.

Even despite the fact that notorious _printf\(const char\* pattern, \.\.\. \)_ is implemented using runtime variadic arguments \(If you don't get it, ellipsis replaces runtime arguments\! Any arguments can be there\!\), this still looks like the biggest kludge in the history of programming\. And how to use it\.\.\. Eh\.\.\. The _\_\_VA\_START\_\__,_ \_\_VA\_COPY\_\__ macros and a bunch of stuff along with it will haunt C developers in their nightmares for ages\. The C\+\+ developers should just exorcise this demon out of the language and add new features for template parameter packs instead\.

## typedef

Everything is simple here, C\+\+ has a perfect replacement for this word\. Just compare:

```cpp
// foo is now alias for void(*)(int) (a pointer to a function)
typedef void(*foo)(int);
// same thing but in C++
using foo = void(*)(int);
```

There's no point in leaving _typedef_ in C\+\+\.\.\.\)\)

## Function\-like macros 

These are macros from C that take arguments\. They are usually the main reason for difficulty of understanding code — bad code, because in modern C\+\+ using such macros is unacceptable\.

So, at this point we removed almost all C from C\+\+ and got almost clean \+\+\. Let's look at what we can remove from them too\.

## Operators new and delete 

Indeed, why do we need these operators if everything has long been moved to the abstraction level of allocators, and we can continue allocating memory on low levels via _malloc_?\! How could anyone bring the system memory allocator to the core language level?

Just look at the list of _new_ overloads \(and I don't even mention the rules\):

<details>
   <summary>SCARY</summary>

![1010_Holy_Cpp/image3.png](https://import.viva64.com/docx/blog/1010_Holy_Cpp/image3.png)


</details>


We only need to leave the _placement new_ to call constructor at the desired address\. Everything else, especially overloads of _new_/_delete_, is strictly prohibited to use in modern C\+\+, unless you want to be bullied\.

## Keyword class

Here's a [link \[RU\]](https://habr.com/en/post/662351/) to my own article about the uselessness of this keyword\. The brief summary is under the spoiler\.

<details>
   <summary>Why is it useless?</summary>

When classes were added to C\+\+, it was the time of OOP, encapsulation, inheritance, all that\. Therefore, it was decided that _class_ is privately inherited by default, and the _class_ fields are also private by default\. Time shows that private inheritance is a rare thing, almost non\-existent in real code\. Besides, you always have something public but not always something private\.

Initially, _struct_ in C didn't have the capabilities of _class_ in terms of adding member functions, constructors, and destructors\. Now _struct_ differs from _class_ solely by these two default parameters, which means each time you use _class_ in your code, you just add a redundant line\.

But _class_ has many more meanings\!

**In a template**: to me, the only use of this feature in 2022 is to confuse the reader\. Although some still use it for the sake of saving 3 letters\. We are not to judge them\.

**In a template** but to declare template parameters — in C\+\+17 this feature has become obsolete\. Now you can write _typename_ without any problems\.

So, to sum up: at the moment there's no real need to use the _class_ keyword in C\+\+, which is funny\.

Wait, there's also _enum class_\! You won't believe it, but _struct_ works here too:

```cpp
enum struct Heh { a, b, c, d };
```

In conclusion, starting with C\+\+, there's not a single contextual use of _class_ where it can't be abandoned or replaced with other keywords\.


</details>
## Keyword final \(prohibition to inherit from the type\) 

Has no helpful use case scenario known to me, breaks the generalized code, the verdict — remove\.

## Virtual functions

They cause a huge number of errors\.

They are inefficient, make you write architecturally bad solutions, use memory poorly, don't allow you to use the rest of the language if the _virtual_ keyword is used\. THE MOST IMPORTANT PART — they can be easily replaced with other core language features without losing functionality but with gained performance, usability, code repetition, checks for compilation, etc\.

Implementation of dynamic polymorphism without virtual functions and their problems: [https://github\.com/kelbon/AnyAny](https://github.com/kelbon/AnyAny) \.

## Member functions \(pointer to the this inside the type implementation\)

C\+\+23 \(finally\) introduced _deducing this_, thanks to which you can declare passing _this_ to the type member functions\. This "member function" will actually be a function from the language's point of view\. Thus, further \(along with deleting virtual functions\) you can get rid of the VERY CONCEPT of member functions and a pointer to this thing \(God forbid you to ever see declaration of the pointer to the member function\)\.

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

At the same time, it is possible that gradually the _this_ keyword will lose its former meaning and only one will remain — the declaration of the explicit passing the type reference/value to the function\.

That's all folks\! We had a wonderful dream about holy C\+\+ and now it's time to write crap with virtual inheritance, forgotten virtual destructor on a polymorphic type, and casts from C\. May the odds be with you\.\.\.