Distinguish between () and {} when constructing object.
C++’s most vexing parse
In the most of the time, use {} to create the object.
But {} will also be treated as std::initializer_listif the class has a constructor Ci that takes std::initializer_list as the argument. And Ci will override the other initializers when you construct the object with {} brackets.
int values. for more details, check https://en.cppreference.com/w/cpp/language/list_initializationclass Widget {
public:
Widget(std::initializer_list<bool> il);
};
Widget w{10, 2}; // Error,

std::vector has two initializers, creating a vector with given size and value , and it also contains a std::list_initializer, but if you write std::vector<int> v{3, 2} . The result is {3, 2} instead of {2, 2, 2}. You need to use braces () to call the other constructor. which is very confusing.<aside>
⚠️ When writing your own constructor, be very careful if you want to add the std::list_initializer since it may break the old code.
</aside>
Prefer nullptr to 0 and NULL
nullptr_t is not a pointer type, nullptr_t is a type of the null pointer literal.0 and NULL cannot be deducted to a pointer type.Prefer alias declarations to typedefs.
using directive instead of typedef to simplify defining a function pointer type.using instead of typedefUse enum class instead of unscoped enum. The enum class is much more strongly typed.
Use = delete to reject invoking of a specific function (copy constructor, fn with specific arguments etc).
For a override function, the name, parameter list, const-ness, must be identical to the base function. And after cpp11, the reference identifer must be identical as well.
override to ensure the compiler can help you find override-related issues (as mentioned above)Prefer to use cbegin, cend when the iterator doesn’t need to do any modification to the data.
Using noexcept when the function doesn’t emit exception
std::vector function push_back can be optimized to use std::move when resizing the vector ONLY IF the move constructor has noexcept keyword.noexceptUse constexpr whenever possible
constexpr on a class/object means the object’s value and type is known at the compile time.constexpr , the following limitations need to be met
constexpr)constexpr function should also be known at compile timeconstexpr functions may only contain one single statement return ..., while C++ 14 allowsmutable keyword
mutable can be modified even in a object/function declared as const
m but the function is still considered const since m is muatbleclass ThreadsafeCounter {
mutable std::mutex m; // The "M&M rule": mutable and mutex go together
int data = 0;
public:
int get() const {
std::lock_guard<std::mutex> lk(m);
return data;
}
void inc() {
std::lock_guard<std::mutex> lk(m);
++data;
}
};
Since const functions might be very likely used in concurrent executions (maybe in the future), make sure your const functions are thread safe (i.e using mutex, atomic to protect concurrent mutable variable modification)
Special member function generation (SKIPPED)
Unique Pointer
Compiler Explorer - C++ (x86-64 gcc 12.1)
unique_ptr can have custom deleters, when using custom deleters, please avoid using capture and use stateless lambda whenever possible. This will make the size of unique_ptr the same as normal raw pointers.
unique_ptr is an move-only type. It cannot be copied.Shared Pointer
shared_ptr is constructed with a raw pointer and a “control block”
shared_ptr from a new object. It will allocate a “control block”shared_ptr , atomic instructions are required.shared_from_this if the object doesn’t have a shared pointer pointed to it will cause undefined behaviorWeak Pointer