Preprocessor directives are lines included in the code of programs preceded by a hash sign (#). These lines are not program statements but directives for the preprocessor. The preprocessor examines the code before actual compilation of code begins and resolves all these directives before any code is actually generated by regular statements.
These preprocessor directives extend only across a single line of code. As soon as a newline character is found, the preprocessor directive is ends. No semicolon (;) is expected at the end of a preprocessor directive. The only way a preprocessor directive can extend through more than one line is by preceding the newline character at the end of the line by a backslash (\).
macro definitions (#define, #undef)
#define identifier replacement
#define TABLE_SIZE 100
int table1[TABLE_SIZE];
int table2[TABLE_SIZE];
// function macro
#include <iostream>
using namespace std;
#define getmax(a,b) ((a)>(b)?(a):(b))
int main()
{
int x=5, y;
y= getmax(x,2);
cout << y << endl;
cout << getmax(7,x) << endl;
return 0;
}
#define TABLE_SIZE 100
int table1[TABLE_SIZE];
#undef TABLE_SIZE
#define TABLE_SIZE 200
int table2[TABLE_SIZE];
This would generate the same code as:
int table1[100];
int table2[200];
Function macro definitions accept two special operators (# and ##) in the replacement sequence: The operator #, followed by a parameter name, is replaced by a string literal that contains the argument passed (as if enclosed between double quotes):
#define str(x) #x
cout << str(test);
The operator ## concatenates two arguments leaving no blank spaces between them:
#define glue(a,b) a ## b
glue(c,out) << "test";
#define AA(a,b)#a###b
int main(int argc,char *argv[])
{
qDebug()<<AA(v1,_m);//v1_m
}
Conditional inclusions (#ifdef, #ifndef, #if, #endif, #else and #elif)
#ifdef TABLE_SIZE
int table[TABLE_SIZE];
#endif
#ifndef TABLE_SIZE
#define TABLE_SIZE 100
#endif
int table[TABLE_SIZE];
Because std::move does nothing but cast its argument to an rvalue, there have been suggestions that a better name for it might have been something like rvalue_cast.
//c++11
template<typename T> // in namespace std
typename remove_reference<T>::type&&
move(T&& param)
{
using ReturnType = // alias declaration;
typename remove_reference<T>::type&&;
return static_cast<ReturnType>(param);
}
// C++14; still in
template<typename T>
decltype(auto) move(T&& param) // namespace std
{
using ReturnType = remove_reference_t<T>&&;
return static_cast<ReturnType>(param);
}
Distinguish universal references from rvalue references.
• If a function template parameter has type T&& for a deduced type T, or if an object is declared using auto&&, the parameter or object is a universal reference. • If the form of the type declaration isn’t precisely type&&, or if type deduction does not occur, type&& denotes an rvalue reference. • Universal references correspond to rvalue references if they’re initialized with rvalues. They correspond to lvalue references if they’re initialized with lvalues.
void f(Widget&& param); // rvalue reference
Widget&& var1 = Widget(); // rvalue reference
auto&& var2 = var1; // is a universal reference
template<typename T>
void f(std::vector<T>&& param); // rvalue reference
template<typename T>
void f(T&& param); // is a universal reference
template<typename T>
void f(std::vector<T>&& param); // param is an rvalue reference
std::vector<int> v;
f(v); // error! can't bind lvalue to
// rvalue reference
auto timeFuncInvocation =
[](auto&& func, auto&&... params) // C++14
{
start timer;
std::forward<decltype(func)>(func)( // invoke func
std::forward<decltype(params)>(params)... // on params
);
stop timer and record elapsed time;
};
Use std::move on rvalue references, std::forward on universal references.
• Apply std::move to rvalue references and std::forward to universal refer‐ ences the last time each is used. • Do the same thing for rvalue references and universal references being returned from functions that return by value. • Never apply std::move or std::forward to local objects if they would other‐ wise be eligible for the return value optimization.
Widget makeWidget() // Moving version of makeWidget
{
Widget w;
…
return std::move(w); // move w into return value
}
Avoid overloading on universal references
std::multiset<std::string> names; // global data structure
void logAndAdd(const std::string& name)
{
auto now = // get current time
std::chrono::system_clock::now();
log(now, "logAndAdd"); // make log entry
names.emplace(name); // add name to global data
}
std::string petName("Darla");
logAndAdd(petName); // pass lvalue std::string
logAndAdd(std::string("Persephone")); // pass rvalue std::string
logAndAdd("Patty Dog"); // pass string literal
in the previous example in second and third call name itself is an lvalue, so it’s copied into names.
template<typename T>
void logAndAdd(T&& name)
{
auto now = std::chrono::system_clock::now();
log(now, "logAndAdd");
names.emplace(std::forward<T>(name));
}
std::string petName("Darla"); // as before
logAndAdd(petName); // as before, copy lvalue into multiset
logAndAdd(std::string("Persephone")); // move rvalue instead of copying it
logAndAdd("Patty Dog"); // create std::string in multiset instead of copying a temporary std::string
Constraining templates that take universal references
Alternatives to the combination of universal references and overloading include the use of distinct function names, passing parameters by lvaluereference-to-const, passing parameters by value, and using tag dispatch. • Constraining templates via std::enable_if permits the use of universal references and overloading together, but it controls the conditions under which compilers may use the universal reference overloads. • Universal reference parameters often have efficiency advantages, but they typically have usability disadvantages.
class Person {
public:
template<
typename T,
typename = typename std::enable_if<!std::is_base_of<Person,typename std::decay<T>::type>::value>::type>
explicit Person(T&& n);
…
};
//c++14
class Person {
public:
template<
typename T,
typename = std::enable_if_t<!std::is_base_of<Person,std::decay_t<T>>::value>>
explicit Person(T&& n);
…
};
#include <type_traits>
template<typename T>
class YourClass {
YourClass() {
// Compile-time check
static_assert(std::is_base_of<BaseClass, T>::value, "type parameter of this class must derive from BaseClass");
// ...
}
}
class Person {
public:
template<typename T,typename = std::enable_if_t<std::is_base_of_v<Person,std::decay_t<T>>>>
explicit Person(T&& n){
}
Person()= default;
};
int main(int argc,char* argv[]){
Person pp;
Person p(pp);
return 0;
}
Distinguish between () and {} when creating objects
class Widget {
public:
Widget(int i, bool b); // ctors not declaring
Widget(int i, double d); // std::initializer_list params
…
};
Widget w1(10, true); // calls first ctor
Widget w2{10, true}; // also calls first ctor
Widget w3(10, 5.0); // calls second ctor
Widget w4{10, 5.0}; // also calls second ctor
class Widget {
public:
Widget(int i, bool b); // as before
Widget(int i, double d); // as before
Widget(std::initializer_list<long double> il); // added
…
};
Widget w1(10, true); // uses parens and, as before,
// calls first ctor
Widget w2{10, true}; // uses braces, but now calls
// std::initializer_list ctor
// (10 and true convert to long double)
Widget w3(10, 5.0); // uses parens and, as before,
// calls second ctor
Widget w4{10, 5.0}; // uses braces, but now calls
// std::initializer_list ctor
// (10 and 5.0 convert to long double)
class Widget {
public:
Widget(int i, bool b); // as before
Widget(int i, double d); // as before
Widget(std::initializer_list<long double> il); // as before
operator float() const; // convert
… // to float
};
Widget w5(w4); // uses parens, calls copy ctor
Widget w6{w4}; // uses braces, calls std::initializer_list ctor (w4 converts to float, and float converts to long double)
Widget w7(std::move(w4)); // uses parens, calls move ctor
Widget w8{std::move(w4)}; // uses braces, calls std::initializer_list ctor (for same reason as w6)
class Widget {
public:
Widget(int i, bool b); // as before
Widget(int i, double d); // as before
// std::initializer_list element type is now std::string
Widget(std::initializer_list<std::string> il);
… // no implicit
}; // conversion funcs
Widget w1(10, true); // uses parens, still calls first ctor
Widget w2{10, true}; // uses braces, now calls first ctor
Widget w3(10, 5.0); // uses parens, still calls second ctor
Widget w4{10, 5.0}; // uses braces, now calls second ctor
class Widget {
public:
Widget(); // default ctor
Widget(std::initializer_list<int> il); // std::initializer
// _list ctor
… // no implicit
}; // conversion funcs
Widget w1; // calls default ctor
Widget w2{}; // also calls default ctor
Widget w3(); // most vexing parse! declares a function!
Widget w4({}); // calls std::initializer_list ctor with empty list
Widget w5{{}}; // ditto
std::vector<int> v1(10, 20); // use non-std::initializer_list
// ctor: create 10-element
// std::vector, all elements have
// value of 20
std::vector<int> v2{10, 20}; // use std::initializer_list ctor:
// create 2-element std::vector,
// element values are 10 and 20
template<typename T, // type of object to create
typename... Ts> // types of arguments to use
void doSomeWork(Ts&&... params)
{
create local T object from params...
…
}
T localObject(std::forward<Ts>(params)...); // using parens
T localObject{std::forward<Ts>(params)...}; // using braces
Prefer nullptr to 0 and NULL
void f(int); // three overloads of f
void f(bool);
void f(void*);
f(0); // calls f(int), not f(void*)
f(NULL); // might not compile, but typically calls
// f(int). Never calls f(void*)
f(nullptr); // calls f(void*) overload
Prefer alias declarations to typedefs
Avoiding such medical tragedies is easy. Introduce a typedef:
typedef std::unique_ptr> UPtrMapSS;
But typedefs are soooo C++98. They work in C++11, sure, but C++11 also offers alias declarations:
using UPtrMapSS = std::unique_ptr>;
// FP is a synonym for a pointer to a function taking an int and
// a const std::string& and returning nothing
typedef void (*FP)(int, const std::string&); // typedef
// same meaning as above
using FP = void (*)(int, const std::string&); // alias
// declaration
template<typename T> // MyAllocList<T>
using MyAllocList = std::list<T, MyAlloc<T>>; // is synonym for
// std::list<T,
// MyAlloc<T>>
MyAllocList<Widget> lw; // client code
template<typename T> // MyAllocList<T>::type
struct MyAllocList { // is synonym for
typedef std::list<T, MyAlloc<T>> type; // std::list<T,
}; // MyAlloc<T>>
MyAllocList<Widget>::type lw; // client code
• typedefs don’t support templatization, but alias declarations do. • Alias templates avoid the “::type” suffix and, in templates, the “typename” prefix often required to refer to typedefs
template <class T>
using remove_const_t = typename remove_const<T>::type;
template <class T>
using remove_reference_t = typename remove_reference<T>::type;
template <class T>
using add_lvalue_reference_t = typename add_lvalue_reference<T>::type;
Prefer scoped enums to unscoped enums
enum Color { black, white, red }; // black, white, red are in same scope as Color
auto white = false; // error! white already declared in this scope
enum class Color { black, white, red }; // black, white, red are scoped to Color
auto white = false; // fine, no other
Color c = white; // error! no enumerator named "white" is in this scope
Color c = Color::white; // fine
auto c = Color::white; // also fine (and in accord with Item 5's advice)
enum Color { black, white, red }; // unscoped enum
std::vector<std::size_t> // func. returning
primeFactors(std::size_t x); // prime factors of x
Color c = red;
…
if (c < 14.5) { // compare Color to double (!)
auto factors = // compute prime factors
primeFactors(c); // of a Color (!)
…
}
enum class Color { black, white, red }; // enum is now scoped
Color c = Color::red; // as before, but
… // with scope qualifier
if (c < 14.5) { // error! can't compare Color and double
auto factors = // error! can't pass Color to
primeFactors(c); // function expecting std::size_t
…
}
if (static_cast<double>(c) < 14.5) { // odd code, but
// it's valid
auto factors = // suspect, but
primeFactors(static_cast<std::size_t>(c)); // it compiles
…
}
Prefer deleted functions to private undefined ones.
template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
public:
…
private:
basic_ios(const basic_ios& ); // not defined
basic_ios& operator=(const basic_ios&); // not defined
};
//instead prefer to use delete
template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
public:
…
basic_ios(const basic_ios& ) = delete;
basic_ios& operator=(const basic_ios&) = delete;
…
};
template<typename T>
void f(T& param); // param is a reference
//and we have these variable declarations,
int x = 27; // x is an int
const int cx = x; // cx is a const int
const int& rx = x; // rx is a reference to x as a const int
//the deduced types for param and T in various calls are as follows:
f(x); // T is int, param's type is int&
f(cx); // T is const int,
// param's type is const int&
f(rx); // T is const int,
// param's type is const int&
universal reference
template<typename T>
void f(T&& param); // param is now a universal reference
int x = 27; // as before
const int cx = x; // as before
const int& rx = x; // as before
f(x); // x is lvalue, so T is int&, param's type is also int&
f(cx); // cx is lvalue, so T is const int&, param's type is also const int&
f(rx); // rx is lvalue, so T is const int&, param's type is also const int&
f(27); // 27 is rvalue, so T is int,param's type is therefore int&&
not pointer or reference
template<typename T>
void f(T param); // param is now passed by value
int x = 27; // as before
const int cx = x; // as before
const int& rx = x; // as before
f(x); // T's and param's types are both int
f(cx); // T's and param's types are again both int
f(rx); // T's and param's types are still both int
template<typename T>
void f(T param); // param is still passed by value
const char* const ptr = // ptr is const pointer to const object "Fun with pointers";
f(ptr); // pass arg of type const char * const
Array Arguments
template<typename T, std::size_t N> // see info
constexpr std::size_t arraySize(T (&)[N]) noexcept // below on
{
return N; // and
}
int keyVals[] = { 1, 3, 7, 9, 11, 22, 35 }; // keyVals has 7 elements
int mappedVals[arraySize(keyVals)]; // so does mappedVals
Auto
auto x = 27; // case 3 (x is neither ptr nor reference)
const auto cx = x; // case 3 (cx isn't either)
const auto& rx = x; // case 1 (rx is a non-universal ref.)
auto&& uref1 = x; // x is int and lvalue, so uref1's type is int&
auto&& uref2 = cx; // cx is const int and lvalue, so uref2's type is const int&
auto&& uref3 = 27; // 27 is int and rvalue, so uref3's type is int&&
const char name[] ="R. N. Briggs"; // name's type is const char[13]
auto arr1 = name; // arr1's type is const char*
auto& arr2 = name; // arr2's type is
void someFunc(int, double); // someFunc is a function; type is void(int, double)
auto func1 = someFunc; // func1's type is void (*)(int, double)
auto& func2 = someFunc; // func2's type is void (&)(int, double)
auto x5 = { 1, 2, 3.0 }; // error! can't deduce T for std::initializer_list<T>
Decltype
const int i = 0; // decltype(i) is const int
bool f(const Widget& w); // decltype(w) is const Widget& decltype(f) is bool(const Widget&)
struct Point {
int x, y; // decltype(Point::x) is int
}; // decltype(Point::y) is int
Widget w; // decltype(w) is Widget
if (f(w)) … // decltype(f(w)) is bool
template<typename T> // simplified version of std::vector
class vector {
public:
…
T& operator[](std::size_t index);
…
};
vector<int> v; // decltype(v) is vector<int>
…
if (v[0] == 0) … // decltype(v[0]) is int&
template<typename Container, typename Index> // works, but requires refinement
auto authAndAccess(Container& c, Index i) -> decltype(c[i])
{
authenticateUser();
return c[i];
}
template<typename Container, typename Index> // C++14;
auto authAndAccess(Container& c, Index i) // not quite
{ // correct
authenticateUser();
return c[i]; // return type deduced from c[i]
}
std::deque<int> d;
…
authAndAccess(d, 5) = 10; // authenticate user, return d[5],
// then assign 10 to it;
// this won't compile!
template<typename Container, typename Index> // C++14; works,but still requires refinement
decltype(auto)
authAndAccess(Container& c, Index i)
{
authenticateUser();
return c[i];
}
Widget w;
const Widget& cw = w;
auto myWidget1 = cw; // auto type deduction: myWidget1's type is Widget
decltype(auto) myWidget2 = cw; // decltype type deduction: myWidget2's type
int i=12;
decltype(i) ii=i;//value
decltype((i)) iii=i;//reference
decltype(auto) iiii=i;//reference
decltype(auto) f1()
{
int x = 0;
…
return x; // decltype(x) is int, so f1 returns int
}
decltype(auto) f2()
{
int x = 0;
…
return (x); // decltype((x)) is int&, so f2 returns int&
}
why auto
template<typename It> // algorithm to dwim ("do what I mean")
void dwim(It b, It e) // for all elements in range from
{ // b to e
while (b != e) {
typename std::iterator_traits<It>::value_type currValue = *b;
}
}
and you can use auto instead
template<typename It> // as before
void dwim(It b, It e)
{
while (b != e) {
auto currValue = *b;
…
}
auto derefUPLess = // comparison func.
[](const std::unique_ptr<Widget>& p1, // for Widgets
const std::unique_ptr<Widget>& p2) // pointed to by
{ return *p1 < *p2; }; // std::unique_ptrs
//use auto for all
auto derefLess = // C++14 comparison
[](const auto& p1, // function for
const auto& p2) // values pointed
{ return *p1 < *p2; }; // to by anything
// pointer-like
Declare functions noexcept if they won’t emit exceptions
Values known during compilation are privileged. They may be placed in read-only memory, for example, and, especially for developers of embedded systems, this can be a feature of considerable importance. Of broader applicability is that integral val‐ ues that are constant and known during compilation can be used in contexts where C++ requires an integral constant expression. Such contexts include specification of array sizes, integral template arguments (including lengths of std::array objects), enumerator values, alignment specifiers, and more. If you want to use a variable for these kinds of things, you certainly want to declare it constexpr, because then com‐ pilers will ensure that it has a compile-time value:
int sz; // non-constexpr variable …
constexpr auto arraySize1 = sz; // error! sz's value not known at compilation
std::array data1; // error! same problem
constexpr auto arraySize2 = 10; // fine, 10 is a compile-time constant
std::array<int, arraySize2> data2; // fine, arraySize2 is constexpr
Note that const doesn’t offer the same guarantee as constexpr, because const objects need not be initialized with values known during compilation:
int sz; // as before
…
const auto arraySize = sz; // fine, arraySize is const copy of sz
std::array<int, arraySize> data; // error! arraySize's value not known at compilation
Simply put, all constexpr objects are const, but not all const objects are constexpr. If you want compilers to guarantee that a variable has a value that can beused in contexts requiring compile-time constants, the tool to reach for is constexpr, not const.
constexpr functions can be used in contexts that demand compile-time con‐ stants. If the values of the arguments you pass to a constexpr function in such a context are known during compilation, the result will be computed during compilation. If any of the arguments’ values is not known during compilation,your code will be rejected.
When a constexpr function is called with one or more values that are not known during compilation, it acts like a normal function, computing its result at runtime. This means you don’t need two functions to perform the same operation, one for compile-time constants and one for all other values. The constexpr function does it all.
constexpr // pow's a constexpr func
int pow(int base, int exp) noexcept // that never throws
{
… // impl is below
}
constexpr auto numConds = 5; // # of conditions
std::array<int, pow(3, numConds)> results; // results has 3^numConds elements
auto base = readFromDB("base"); // get these values
auto exp = readFromDB("exponent"); // at runtime
auto baseToExp = pow(base, exp); // call pow function at runtime
constexpr int pow(int base, int exp) noexcept
{
return (exp == 0 ? 1 : base * pow(base, exp - 1));
}
constexpr functions are limited to taking and returning literal types, which essentially means types that can have values determined during compilation. In C++11, all built-in types except void qualify, but user-defined types may be literal, too, because constructors and other member functions may be constexpr:
In C++11, two restrictions prevent Point’s member functions setX and setY from being declared constexpr. First, they modify the object they operate on, and in C++11, constexpr member functions are implicitly const. Second, they have void return types, and void isn’t a literal type in C++11. Both these restrictions are lifted in C++14, so in C++14, even Point’s setters can be constexpr:
class Point {
public:
…
constexpr void setX(double newX) noexcept // C++14
{ x = newX; }
constexpr void setY(double newY) noexcept // C++14
{ y = newY; }
…
};
constexpr Point reflection(const Point& p) noexcept
{
Point result; // create non-const Point
result.setX(-p.xValue()); // set its x and y values
result.setY(-p.yValue());
return result; // return copy of it
}
constexpr Point p1(9.4, 27.7); // as above
constexpr Point p2(28.8, 5.3);
constexpr auto mid = midpoint(p1, p2);
constexpr auto reflectedMid = // reflectedMid's value is
reflection(mid); // (-19.1 -16.5) and known during compilation
The table shows you the constructors and destructors of a container. A std:vector stands for the rest of them.
Type
Example
Default
std::vector<int> vec1
Range
std::vector<int> vec2(vec1.begin(), vec1.end())
Copy
std::vector<int> vec3(vec2)
Copy
std::vector<int> vec3= vec2
Move
std::vector<int> vec4(std::move(vec3))
Move
std::vector<int> vec4= std::move(vec3)
Sequence (Initializer list)
std::vector<int> vec5 {1, 2, 3, 4, 5}
Sequence (Initializer list)
std::vector<int> vec5= {1, 2, 3, 4, 5}
Destructor
vec5.~vector()
Delete elements
vec5.clear()
Because std::array is generated at compile time, there are a few things that are special. std::array has no move constructor and can neither be created with a range nor with an initialiser list. However, you can initialize a std::array with an aggregate initialisation. Also, std::array has no method for removing its elements.
template<class InputIterator, class OutputIterator>
OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result)
{
while (first!=last) {
*result = *first;
++result; ++first;
}
return result;
}
access
Iterator
Description
cont.begin() and cont.end()
Pair of iterators to iterate forward.
cont.cbegin() and cont.cend()
Pair of iterators to iterate const forward.
cont.rbegin() and cont.rend()
Pair of iterators to iterate backward.
cont.crbegin() and cont.crend()
Pair of iterators to iterate const backward.
assign new elements You can assign new elements to existing containers or swap two containers. For the assignment of a container cont2 to a container cont, there exists the copy assignment cont= cont2 and the move assignment cont= std::move(cont2). A special form of assignment is the one with an initialiser list: cont= {1, 2, 3, 4, 5}. That’s not possible for std::array, but you can instead use the aggregate initialisation. The function swap exists in two forms. You have it as a method cont(swap(cont2)) or as a function template std::swap(cont, cont2).
comparison operators Containers support the comparison operators ==, !=, <, >, <=, >=. The comparison of two containers happens on the elements of the containers. If you compare associative containers, their key is compared. Unordered associative containers support only the comparison operator == and !=.
Sequential Container
Criteria
array
vector
deque
list
forward_list
Size
static
dynamic
dynamic
dynamic
dynamic
Implementation
static array
dynamic array
sequence of arrays
doubled linked list
single linked list
Access
random
random
random
forward and backward
forward
Optimized for insert and delete at
end: O(1)
begin and end: O(1)
begin and end: O(1)
begin(1)
arbitrary: O(1)
arbitrary: O(1)
Memory reservation
yes
no
no
no
Release of memory
shrink_to_fit
shrink_to_fit
always
always
Strength
no memory allocation; minimal memory requirements
95% solution
insertion and deletion at the begin and end
insertion and deletion at an arbitrary position
fast insertion and deletion; minimal memory requirements
Weakness
no dynamic memory
Insertion and deletion
Insertion and deletion
no random access
no random access
memory allocation
at an arbitrary position: O(n)
at an arbitrary position: O(n)
vector
std::vector is a homogeneous container, for which it’s length can be adjusted at runtime. std::vector needs the header <vector>. As it stores its elements contiguously in memory, std::vector support pointer arithmetic.
deque
std::deque, which consists of a sequence of arrays, is quite similar to std::vector. std::deque need the header <deque>. The std::deque has three additional methods deq.push_front(elem), deq.pop_front() and `deq.emplace_front(args… ) to add or remove elements at its beginning.
list
std::list is a doubled linked list. std::list needs the header <list>.
Although it has a similar interface to std::vector or std::deque, std::list is quite different to both of them. That’s due to its structure.