The following are some features that added to the C++11 standard and are useful in programming.
auto
At the compile-time type of variable is deduced from the initializer. There is no need to declare the type of variable.
In following example vector<int>::iterator is replaced with auto.
#include <iostream>
#include <vector>
using namespace std;
double add(int a, int b) {
double c = a + b;
return c;
}
int main() {
vector<int> vec = {1, 2, 3, 4, 5};
auto a = 1; // type of a is int
auto b = 1.1; // type of b is float
auto c = add(123, 46); // type of c is double
auto d = 'a'; // type of d is char
auto e = vec.begin(); // type of e is vector<int>::iterator
auto &a1 = a; // type of a1 is reference to int
int sum = 0;
for (auto i = vec.begin(); i != vec.end(); i++) {
sum += *i;
}
cout << "sum of elements is " << sum << "\n";
}
We can see the code is more readable. We can also get variables by reference by auto& instead of auto.
range based for loop
With help of range based for loop can be written as
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> vec = {1, 2, 3, 4, 5};
int sum = 0;
for (auto i:vec) {
sum += i;
}
cout << "sum of elements is " << sum << "\n";
}
lambda
Lambda is an unnamed function object capable of capturing variables in scope.
Lambda syntax: [capture clause] (parameter list) {lambda body}
Capture clause | meaning |
---|---|
[ ] (…){..} | Access no variables |
[=] (…){..} | Capture variables by value |
[&] (…){..} | Capture variables by reference |
[n, &m] (…){..} | For each variable, decide capture type (value or reference) |
The following code shows each capture example.
#include <iostream>
using namespace std;
int main() {
// lambda to print value
auto print = [](int a) { std::cout << "printing: " << a << "\n"; };
print(1234); // print 1234
// lambda to add values
auto add = [](int a, int b) { return a + b; };
cout << "5 + 3 = " << add(5, 3) << "\n"; // 5 + 3 = 8
int n = 10;
int m = 12;
// capture variables by value
auto printValue = [=]() { std::cout << "n = " << n << "\n"; };
printValue(); // n = 10
// capture variables by reference
auto printRef = [&]() { std::cout << "n = " << n << "\n"; };
printRef(); // n = 10
// capture individual variable
auto printPass = [n, &m]() { // capture n by value, m by reference
std::cout << "n = " << n << "\n"
<< "m = " << m << "\n";
};
printPass(); // n = 10 m = 12
return 0;
}
Variables capture by value can not be modified inside the lambda using the mutable keyword allows modification inside the lambda.
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n = 10;
// modify value in lambda
std::cout << "n = " << n << "\n"; // n = 10
// auto incN = [=]() { n++; }; // error can not assign in non-mutable lambda
auto incN = [=]() mutable { n++; };
incN();
std::cout << "n = " << n << "\n"; // n = 10
return 0;
}
Lambda is convenient to use with standard library algorithms.
The following is an example of sorting elements of a vector by absolute values.
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <vector>
using namespace std;
int main() {
std::vector<int> v = {-1, 3, 76, 9, 5, 3, 55, 7, -4, -7, -9, -1};
// sort vector by absolute value
std::sort(v.begin(), v.end(), [](int a, int b) { return abs(a) < abs(b); });
for (auto &i : v) {
std::cout << i << " "; // -1 -1 3 3 -4 5 7 -7 9 -9 55 76
}
return 0;
}
trailing return type
C++11 introduced a new way to declare the return value of a function. This new way is useful in lambda and template programming.
#include <iostream>
using namespace std;
// trailing return type function example
auto mul(int a, int b) -> int { return a * b; }
int main() {
std::cout << mul(2, 4) << "\n";
// use of trailing return type in lambda
auto add = [](int a, int b) -> int { return a + b; };
return 0;
}
decltype
Using template and lambda with auto sometimes it is difficult to deduce the return type without knowing the argument types, in this situation decltype becomes useful. decltype returns type of variable or expression
#include <iostream>
#include <string>
using namespace std;
// use of decltype in template
template <typename A, typename B>
auto add(A a, B b) -> decltype(a + b) {
return a + b;
}
int main() {
int a = 1;
decltype(a) b = a * 5; // declare variable b of same type as a
cout << add(4, 6) << "\n"; // return int
cout << add(4, 6.1) << "\n"; // return float
cout << add(4.1, 6) << "\n"; // return float
cout << add(4.1, 6.1) << "\n"; // return float
auto mul = [](int a, int b) -> int { return a * b; };
decltype(mul) newMul = mul; // the type of a lambda is unique and unnamed
cout << mul(2, 4) << "\n";
cout << newMul(2, 5) << "\n";
return 0;
}
override
C++11 introduced the new keyword override, which at compile time checks whether the method overridden is virtual in parent class, if not then it throw the error. It act as safety check at compile time, whether overridden method is virtual or not in parent class.
#include <iostream>
#include <string>
using namespace std;
class A {
public:
A() {}
virtual ~A() {}
virtual void method1() { cout << "method1 A\n"; }
void method2() { cout << "method2 A\n"; }
};
class B : A {
public:
B() {}
virtual ~B() {}
void method1() override { cout << "method1 B\n"; }; // OK
void
method2() { // error only virtual functions can be marked override
cout << "method2 B\n";
};
};
final
‘final’ ensures when used in virtual function declaration or definition that marked function can not be overridden into the derived class otherwise compile time error is generated.
‘final’ ensures when used in class definition that no class can be derived from that class otherwise compile time error is generated.
class Base {
public:
virtual void foo() {}
};
class A : Base {
void foo() final{}; // mark method as final
void bar() final{}; // error as it is non virtual method
};
class B final : A { // make class final
void foo() override{}; // error foo overrides final function
};
class C : B { // error class B is marked as final
};