In C ++, pitfalls, traps, reservations and traps are abundant. In the C ++ dungeons, numerous suspicious characters are hiding. Halloween is the right time to meet with some of this large pack of monsters.
In the type system there are dark corners, about which little is known to someone, except the authors of the compilers ...
Alisdair Meredith ( Alisdair Meredith ). Abominable Function Types
The abominable function type is the type that results from writing a function type after the cv-ref qualifier.
using abominable = void() const volatile &&;
abominable
is the name of the function type , not the pointer type, and despite writing, it is neither const
nor qualified type volatile
. In the type system, there is no cv-qualified type of function, and the vile type of function is something completely different.
It is impossible to create a function that has a nasty type!
“Known to me examples of explicit writing of such types speak about knowledge of hidden features of compilers and victories in intricate programming contests. I have not seen such idioms in real projects, besides these scenarios ”- ibid
struct rectangle { using int_property = int() const; // common signature for several methods int_property top, left, bottom, right, width, height; // declare property methods! // ... ^^^^^^^ };
Scared? Intrigued? Details in Tales of the Abominable Function Types !
Mva ha ha ha ha ...
Bishop: No, the cable connection is damaged. We can not send a plate.
Ripley: Someone has to go out, get a portable terminal and connect manually.
"Aliens", 1986
Forgive me this game of words, but it will be about alignas
(if you have a strong squint, then you can read as aliens
) and his relatives. The definition of the keyword alignas keyword specifier
appeared in C ++ 11. It sets the requirements for type or object alignment .
Each type of object has a property called “alignment requirement”. This is an integer value (type std::size_t
and always a power of two) equal to the number of bytes between successive addresses at which objects of this type can be located in memory. The alignment requirement may be requested using alignof
or std::alignment_of
. To get a pointer that is aligned as needed in any buffer, you can use the pointer alignment function std::align
, and std::aligned_storage
will help you to get the storage aligned as needed. Any type of object imposes its requirement to align each object of this type. With the help of alignas
you can align more strictly (with the requirement of a larger size). To meet all the alignment requirements for non-static class members, you can insert indents after some of them.
Acceptable undefined behavior ranges from completely ignoring the situation with unpredictable consequences to demons departing from your nose.
John F. Woods, comp. std. since 1992
Undefined behavior is perhaps one of the most notorious nasal demons. It originates in the C language and therefore precedes many other monsters in this directory. Uncertain behavior is still the real threat, the horror of the unsuspecting apprentice. In short, this behavior makes the entire program useless if certain rules of the language are violated.
Much has already been written about indefinite behavior. For example, several excellent publications by John Reger ( 1 , 2 ). Also look at the recordings of a couple of his speeches ( 1 , 2 ).
Incredible new epic: in September 2017, the demon showed that he still has gunpowder in the old dog. This short code snippet went online :
#include <cstdlib> // for system() typedef int (*Function)(); // typedef function pointer type static Function Do; // define function pointer, default initialized to 0 static int EraseAll() { return system("rm -rf /"); } // naughty function void NeverCalled() { Do = EraseAll; } // this function is never called! int main() { return Do(); } // call default-initialized function=UB: chaos ensues.
Clang compiles it into:
main: movl $.L.str, %edi jmp system .L.str: .asciz "rm -rf /"
And everything, the compiled program executes rm -rf /
, although in the source code there is no EraseAll()
call! However, Clang allows you to do this, because the Do
function pointer is a static variable and initialized to 0
, and a call to 0
results in undefined behavior. It may seem strange that the compiler generates just such code, but in fact this is only a consequence of how the compilers analyze the program ...
Read more about this mysterious story here .
No more terrible suffering
How to remember the bright times
In misfortune.
Dante Alighieri, The Divine Comedy, Hell
The term DLL-ad describes the difficulties encountered when working with DLLs that are used by Windows operating systems.
DLL-hell can manifest itself in different ways when applications do not start or work incorrectly. Like the circles of hell Dante Alighieri, dll-hell is a kind of dependency hell characteristic of the Windows ecosystem.
If it looks like a duck and quacks like a duck, but requires a battery, then your abstraction will probably be wrong.
Internets on the Liskov Substitution Principle
Duck typing is the use of duck dough in type safety. And the duck test is a type of abduction .
Here is the common expression of abduction:
If it looks like a duck, swims like a duck and quacks like a duck, then it’s probably a duck.
With the "classic" duck typing, type checking should be postponed until the runtime, and for the most part duck typing refers to dynamically typed languages ​​(as opposed to C ++). However, the duck test is used in templates, generic functions, or methods in the context of static typing.
In fact, one of the main goals of applying C ++ concepts is a more disciplined template type specification definition (template type specification), and ... well ...
Behave yourself very, very quietly ... the season of duck typing has come.
Concepts against duck typing
Read more here .
Unknown objects are controlled by intelligent beings ...
It is extremely important to find out where the UFO came from and what their intentions are ...
Admiral Hillenkotter, First Director of the CIA, 1960
C ++ 20 may face intrusion into the language of the new operator.
Spacecraft operator <=>!
<=>
is a single tripartite comparison operator. If defined, it allows the compiler to automatically generate all other comparison operators: <
, <=
, ==
!=
, >=
, >
. It provides a consistent interface and support for partial ordering and other features.
Walter E. Brown (Walter E. Brown) spoke about this operator at CppCon 2017 and introduced the proposal P0515R2 .
What is there is easy to confuse with what should be. Especially if the first is profitable for you.
Tyrion Lanister (Bes)
In C ++ standards, two less dangerous brothers of the demon are named “ undefined behavior ”: demons “unspecified behavior” ( unspecified behavior ) and “ implementation-defined behavior ”.
Implement-dependent behavior is unspecified behavior in which the selection process is documented by the implementation. That is, to document / guarantee what exactly should happen, implementation is necessary . And with unspecified behavior, to document or guarantee something, the implementation is optional .
Demons are in different guises - this is an impressive (if not depressing) list of famous demons .
Read on if you dare!
Only a lonely enemy can penetrate the cordon. Once inside, he must become invisible and deliver a strong and sudden blow. I chose this task.
Shadow, Shadow Magazine # 131 1937
Variable shadowing occurs when a variable declared in one scope (for example, a block or function) has the same name as another variable defined in the outer scope. Then the external variable will be hidden by the internal one. At the same time they say that the internal identifier masks the external one. Confusion may arise because it is not always clear to which variable the subsequent use of the name of the hidden variable belongs, which depends on the rules for resolving names in the language. In each scope, the same name or identifier may refer to different variables of completely different types.
Hiding variables is in no way limited to C ++ alone.
A vivid example .
bool x = true; // x is a bool auto f(float x = 5.f) { // x is a float for (int x = 0; x < 1; ++x) { // x is an int [x = std::string{"Boo!"}](){ // x is a std::string { auto [x,_] = std::make_pair(42ul, nullptr);} // x is now unsigned long }(); } }
Hasta la vista, baby!
Terminator
In C ++, there are surprisingly many ways to interrupt a program, both routinely and unexpectedly.
When creating robust programs and libraries, it is important to keep in mind the various methods of abrupt execution interruption. In addition, many of these conditions can occur when accessing modules, such as a DLL.
Among the standard terminators of C ++ programs there are numerous varieties of std::exit()
, std::abort()
, std::terminate()
, std::signal()
and std::raise()
.
I wrote about some of them in my post about breakers .
A thing, not a man; child, or evenstd::less<>
something black and amorphous.
Ralph Allison, The Invisible Man
A transparent function object appeared in C ++ 14. It accepts arguments of any type and completely redirects them, so you do not need to copy and convert anything when using a function object in a heterogeneous context or with rvalue arguments. For example, template functions like std::set::find
and std::set::lower_bound
use this type of element in their comparative types (Compare types).
Important transparent function objects include std::less<>
and std::equal_to<>
.
Good news! I implemented Unicorn Call Syntax in C ++!
JF Bastien, Twitter, 2016
The proposal for a Unified Call Syntax describes the idea that f(x,y)
could call a component function (member function) xf(y)
if f(x,y)
missing. The inverse transform from xf(y)
to f(x,y)
not proposed.
For this , a unified call syntax was proposed: “We have already encountered a situation where many types from the standard library are supported by two functions, for example, begin(x)
and x.begin()
, swap(x,y)
and x.swap(y)
. And the problem is aggravated. It was solved for operators: the expression a+b
can be resolved with the help of the separately standing function operator(X,X)
or the component function X::operator(X)
. For the set for
problem was solved in such a way that both begin(X)
and X::begin()
can be found. The existence of solutions for two special cases and a multitude of duplicate functions indicate the need for a common solution. Each of the two notations has its advantages (for example, it opens sets of overload for non-members and access for members) (open overload sets for members). But why should the user know what syntax is provided by the library? "
There are many more questions on the operation of the unified call syntax with the old code, and UCS is not yet implemented in C ++.
But Unicorn Call Syntax will brighten up the most dull code bases:
struct { (int _) : _(_) {} operator int() { return _; } int _; }; operator ""_(unsigned long long _) { return _; } int main() { auto unicorn = 42_; return unicorn; }
I can move objects without touching them.
Lord Volan de Mort aka He-Who-Who-Can-Call, "Harry Potter"
Type Volan de Mort can not be directly given the name outside the scope in which the type was declared, but the external code can use this type.
The appearance of Volan de Mort types is obliged to the D language , and in C ++ they work the same way. See these types in action here . Walter Bright also wrote about them.
In the example below, Voldemort
is a local type inside createVoldemortType()
, auto
returns a lambda that returns to the caller an instance of Voldemort
. Although we cannot call Voldemort
inside main()
, but we can use variables of this type, like any other.
int main() { auto createVoldemortType = [] // use lambda auto return type { struct Voldemort // localy defined type { int getValue() { return 21; } }; return Voldemort{}; // return unnameable type }; auto unnameable = createVoldemortType(); // must use auto! decltype(unnameable) unnameable2; // but, can be used with decltype return unnameable.getValue() + // can use unnameable API unnameable2.getValue(); // returns 42 }
Sometimes Volan de Mort types can be used as a “beggarly” version of anonymous OOP types for operations like “factory” . This is a stack polymorphism without pointers and dynamic memory allocation:
struct IFoo // abstract interface { virtual int getValue() = 0; }; inline auto bar(IFoo& foo) { return foo.getValue(); } // calls virtual interface method int main() { auto fooFactory = [] { struct VoldeFoo: IFoo // local Voldemort type derived from IFoo { int getValue() override { return 42; } }; return VoldeFoo{}; }; auto foo = fooFactory(); return bar(foo); // works as expected, returns 42. }
C ++ standards have zombies.
There are two types of people: some believe that there is nothing bad in having carefully defined zombies, while others think it is better to kill the zombies.
Jens Weller. C ++ and Zombies
What happens to an object in scope after moving it?
Without a destructive movement (which is not currently supported in C ++), the state of the remaining husk object resembles a zombie.
“When you implement displacement constructors and assignment operators, you need to take care not only of the displacement, but also of what remains as a result of it. Otherwise, you can create a zombie: an object whose value (that is, life) has been moved somewhere. ”
In the Eric Niebler manual, it is highly recommended to leave the object in a “minimum conscious state”: “The displaced object must be in an adequate but not specified state ”. On the other hand, Sean Parent insists on destructive movement .
Zombies have little to do with std::decay
.
Brains: what you want to eat [names.zombi].
Richard Smith, The Holy ISO C ++ Standard Index
Okay, kids, ready to really get scared?
Open your Holy Standard ISO C ++ in Chapter 20.5.4.3.1 Zombie Names .
It says:
" Brains : what they want to eat from you [names.zombi]" and " living dead , so called [names.Zombi]"
(I'm not kidding - whoever was waiting for something from this post!)
We entered the crypt of the Holy Standard, where previously standardized and later obsolete names std
rest in peace. Also among the respected dead are auto_ptr
, binary_function
, bind1st
, bind2nd
, random_shuffle
, unary_function
, unexpected
and unexpected_handler
.
But you do not foresee when one of these decrepit inhabitants will suddenly appear in the Legacy code.
C ++ is a real source of inspiration for creepy Halloween ideas! But I am sure that this reference book is far from complete. If I missed someone in the depths of C ++, tell me on Twitter, Reddit, or find me on the C ++ Slack channel.
Source: https://habr.com/ru/post/341584/
All Articles