
In a recent
post, habrovchane voted for the chapters from the book to be translated into Russian. A little thought, I decided to cheat, and instead of translating the chapters from the open access, to tell about what is in the closed.
In order not to violate the rights of publishers, this will not be a literal translation, but a squeeze from those techniques that may seem interesting even to people who do not work with Boost.
What awaits you under the cut:
')
- Avoid macro calls instead of functions, for example, max / min.
- We call the optimal function, using the example of std :: swap and its specialization in different namespaces.
- Faster insertion into std :: vector.
- Destructors in C ++ 11.
Avoid macro calls instead of functions, for example, max / min.
Those who work a lot with Visual Studio must have come across the fact that min / max are macros. Because of this, practically any min / max functions in any classes and namespaces are treated by the preprocessor as macro substitutions. As a consequence, the following code examples do not compile:
int max_int = std::numeric_limits<int>::max(); my_class_variable.max();
There are various ways to avoid macro calls instead of functions, but the most portable and short is to simply enclose the entire function call in parentheses (except for the parentheses themselves):
int max_int = (std::numeric_limits<int>::max)(); (my_class_variable.max)();
Now the preprocessor will not accept max as a macro call and the code will compile correctly. This trick works with all macros, not only with min / max, and is often used in Boost.
We call the optimal function, using the example of std :: swap and its specialization in different namespaces.
Imagine the situation: you need to exchange the values of two variables in the most efficient way. Usually, swap functions are used for this. But the trouble is, we write a generalized code that should work with custom types:
template <class T> void my_function(T& value1, T& value1) {
The non-optimal solution is that custom types can have their own swap function, which works much more efficiently than the standard version:
namespace some_namespace { class my_vector; void swap(my_vector& value1, my_vector& value2); }
A very simple solution to the problem is to fix the code as follows:
template <class T> void my_function(T& value1, T& value1) { using std::swap;
Now the compiler will first try to find the swap function from the parameter namespaces value1 and value2. In other words, if value1 and value2 are instances of the class some_namespace :: my_vector, then the compiler will first try to use the swap from some_namespace. If the parameter spaces value1 and value2 do not have a swap function, the compiler will use std :: swap.
Why is it that way? Because the compiler must perform an Argument Dependent Lookup (aka Koenig Lookup) before attempting to make a function call from using. This trick is used by boost :: swap, and boost :: numeric_cast uses a similar approach for the functions floor, ceil, etc.
Faster insertion into std :: vector.
Let's start with a rather trivial advice: if you know the number of elements that will be inserted into a vector, then before insertion you must call
reserve (the number of elements to insert) :
std::vector<int> numbers; numbers.reserve(1000); for (size_t i = 0; i < 1000; ++i) numbers.push.back(i);
This advice is available in many programming tutorials, but for some reason it is often forgotten. From recent examples where they forgot about it - Unity code (Ubuntu GUI).
Now less trivial advice. In C ++ 11, it was decided that std :: vector and a number of other containers can use move constructors and move-assignment operators for elements
only if move constructors and move-assignment operators of these elements do not throw exceptions (well, or if the elements do not can be copied).
In other words, in C ++ 11, for best performance, you should mark constructors and operators that do not throw exceptions, as
noexcept .
In addition to containers from the standard library, many classes from Boost are sensitive to noexcept, for example, boost :: variant and boost :: circular_buffer.
Destructors in C ++ 11.
Even before the C ++ 11 standard, throwing exceptions in destructors was considered a very bad tone. In C ++ 11, this leads to the death of the application.
In C ++ 11,
all object destructors are automatically marked as
noexcept . This leads to the fact that if you have a class that throws an exception in the destructor, then in C ++ 11, std :: terminate () will be called in this case and the entire application will terminate without calling the destructors for the created objects.
Of course, you can get around this, explicitly tell the compiler that the destructor throws an exception ... But is it not better to do everything for good?
Instead of totals
The book has many interesting things in C ++ and Boost, you can talk for a long time. What would you like to hear first: