mirror of
https://github.com/AnthonyCalandra/modern-cpp-features.git
synced 2025-12-18 02:24:35 +03:00
Cleanup; consistent formatting.
This commit is contained in:
83
CPP11.md
83
CPP11.md
@@ -137,9 +137,9 @@ int sum(const std::initializer_list<int>& list) {
|
||||
return total;
|
||||
}
|
||||
|
||||
auto list = { 1, 2, 3 };
|
||||
auto list = {1, 2, 3};
|
||||
sum(list); // == 6
|
||||
sum({ 1, 2, 3 }); // == 6
|
||||
sum({1, 2, 3}); // == 6
|
||||
sum({}); // == 0
|
||||
```
|
||||
|
||||
@@ -198,7 +198,7 @@ A `lambda` is an unnamed function object capable of capturing variables in scope
|
||||
```c++
|
||||
int x = 1;
|
||||
|
||||
auto getX = [=]{ return x; };
|
||||
auto getX = [=] { return x; };
|
||||
getX(); // == 1
|
||||
|
||||
auto addX = [=](int y) { return x + y; };
|
||||
@@ -215,7 +215,7 @@ auto f1 = [&x] { x = 2; }; // OK: x is a reference and modifies the original
|
||||
|
||||
auto f2 = [x] { x = 2; }; // ERROR: the lambda can only perform const-operations on the captured value
|
||||
// vs.
|
||||
auto f3 = [x] () mutable { x = 2; }; // OK: the lambda can perform any operations on the captured value
|
||||
auto f3 = [x]() mutable { x = 2; }; // OK: the lambda can perform any operations on the captured value
|
||||
```
|
||||
|
||||
### decltype
|
||||
@@ -245,10 +245,10 @@ Semantically similar to using a `typedef` however, template aliases with `using`
|
||||
```c++
|
||||
template <typename T>
|
||||
using Vec = std::vector<T>;
|
||||
Vec<int> v{}; // std::vector<int>
|
||||
Vec<int> v; // std::vector<int>
|
||||
|
||||
using String = std::string;
|
||||
String s{"foo"};
|
||||
String s {"foo"};
|
||||
```
|
||||
|
||||
### nullptr
|
||||
@@ -327,7 +327,7 @@ struct Foo {
|
||||
Foo() : Foo(0) {}
|
||||
};
|
||||
|
||||
Foo foo{};
|
||||
Foo foo;
|
||||
foo.foo; // == 0
|
||||
```
|
||||
|
||||
@@ -401,16 +401,16 @@ A more elegant, efficient way to provide a default implementation of a function,
|
||||
struct A {
|
||||
A() = default;
|
||||
A(int x) : x(x) {}
|
||||
int x{ 1 };
|
||||
int x {1};
|
||||
};
|
||||
A a{}; // a.x == 1
|
||||
A a2{ 123 }; // a.x == 123
|
||||
A a; // a.x == 1
|
||||
A a2 {123}; // a.x == 123
|
||||
```
|
||||
|
||||
With inheritance:
|
||||
```c++
|
||||
struct B {
|
||||
B() : x(1);
|
||||
B() : x(1) {}
|
||||
int x;
|
||||
};
|
||||
|
||||
@@ -419,7 +419,7 @@ struct C : B {
|
||||
C() = default;
|
||||
};
|
||||
|
||||
C c{}; // c.x == 1
|
||||
C c; // c.x == 1
|
||||
```
|
||||
|
||||
### Deleted functions
|
||||
@@ -434,7 +434,7 @@ public:
|
||||
A& operator=(const A&) = delete;
|
||||
};
|
||||
|
||||
A x{ 123 };
|
||||
A x {123};
|
||||
A y = x; // error -- call to deleted copy constructor
|
||||
y = x; // error -- operator= deleted
|
||||
```
|
||||
@@ -442,14 +442,14 @@ y = x; // error -- operator= deleted
|
||||
### Range-based for loops
|
||||
Syntactic sugar for iterating over a container's elements.
|
||||
```c++
|
||||
std::array<int, 5> a{ 1, 2, 3, 4, 5 };
|
||||
std::array<int, 5> a {1, 2, 3, 4, 5};
|
||||
for (int& x : a) x *= 2;
|
||||
// a == { 2, 4, 6, 8, 10 }
|
||||
```
|
||||
|
||||
Note the difference when using `int` as opposed to `int&`:
|
||||
```c++
|
||||
std::array<int, 5> a{ 1, 2, 3, 4, 5 };
|
||||
std::array<int, 5> a {1, 2, 3, 4, 5};
|
||||
for (int x : a) x *= 2;
|
||||
// a == { 1, 2, 3, 4, 5 }
|
||||
```
|
||||
@@ -488,10 +488,10 @@ struct A {
|
||||
A(int, int, int) {}
|
||||
};
|
||||
|
||||
A a{0, 0}; // calls A::A(int, int)
|
||||
A a {0, 0}; // calls A::A(int, int)
|
||||
A b(0, 0); // calls A::A(int, int)
|
||||
A c = {0, 0}; // calls A::A(int, int)
|
||||
A d{0, 0, 0}; // calls A::A(int, int, int)
|
||||
A d {0, 0, 0}; // calls A::A(int, int, int)
|
||||
```
|
||||
|
||||
Note that the braced list syntax does not allow narrowing:
|
||||
@@ -501,7 +501,7 @@ struct A {
|
||||
};
|
||||
|
||||
A a(1.1); // OK
|
||||
A b{1.1}; // Error narrowing conversion from double to int
|
||||
A b {1.1}; // Error narrowing conversion from double to int
|
||||
```
|
||||
|
||||
Note that if a constructor accepts a `std::initializer_list`, it will be called instead:
|
||||
@@ -513,10 +513,10 @@ struct A {
|
||||
A(std::initializer_list<int>) {}
|
||||
};
|
||||
|
||||
A a{0, 0}; // calls A::A(std::initializer_list<int>)
|
||||
A a {0, 0}; // calls A::A(std::initializer_list<int>)
|
||||
A b(0, 0); // calls A::A(int, int)
|
||||
A c = {0, 0}; // calls A::A(std::initializer_list<int>)
|
||||
A d{0, 0, 0}; // calls A::A(std::initializer_list<int>)
|
||||
A d {0, 0, 0}; // calls A::A(std::initializer_list<int>)
|
||||
```
|
||||
|
||||
### Explicit conversion functions
|
||||
@@ -530,11 +530,11 @@ struct B {
|
||||
explicit operator bool() const { return true; }
|
||||
};
|
||||
|
||||
A a{};
|
||||
A a;
|
||||
if (a); // OK calls A::operator bool()
|
||||
bool ba = a; // OK copy-initialization selects A::operator bool()
|
||||
|
||||
B b{};
|
||||
B b;
|
||||
if (b); // OK calls B::operator bool()
|
||||
bool bb = b; // error copy-initialization does not consider B::operator bool()
|
||||
```
|
||||
@@ -570,11 +570,10 @@ class Human {
|
||||
// Default initialization on C++11
|
||||
class Human {
|
||||
private:
|
||||
unsigned age{0};
|
||||
unsigned age {0};
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
### Right angle Brackets
|
||||
C++11 is now able to infer when a series of right angle brackets is used as an operator or as a closing statement of typedef, without having to add whitespace.
|
||||
|
||||
@@ -598,7 +597,7 @@ typename remove_reference<T>::type&& move(T&& arg) {
|
||||
|
||||
Transferring `std::unique_ptr`s:
|
||||
```c++
|
||||
std::unique_ptr<int> p1{ new int };
|
||||
std::unique_ptr<int> p1 {new int{0}};
|
||||
std::unique_ptr<int> p2 = p1; // error -- cannot copy unique pointers
|
||||
std::unique_ptr<int> p3 = std::move(p1); // move `p1` into `p3`
|
||||
// now unsafe to dereference object held by `p1`
|
||||
@@ -625,11 +624,11 @@ struct A {
|
||||
|
||||
template <typename T>
|
||||
A wrapper(T&& arg) {
|
||||
return A{ std::forward<T>(arg) };
|
||||
return A{std::forward<T>(arg)};
|
||||
}
|
||||
|
||||
wrapper(A{}); // moved
|
||||
A a{};
|
||||
A a;
|
||||
wrapper(a); // copied
|
||||
wrapper(std::move(a)); // moved
|
||||
```
|
||||
@@ -644,11 +643,12 @@ void foo(bool clause) { /* do something... */ }
|
||||
|
||||
std::vector<std::thread> threadsVector;
|
||||
threadsVector.emplace_back([]() {
|
||||
// Lambda function that will be invoked
|
||||
// Lambda function that will be invoked
|
||||
});
|
||||
threadsVector.emplace_back(foo, true); // thread will run foo(true)
|
||||
for (auto& thread : threadsVector)
|
||||
thread.join(); // Wait for threads to finish
|
||||
for (auto& thread : threadsVector) {
|
||||
thread.join(); // Wait for threads to finish
|
||||
}
|
||||
```
|
||||
|
||||
### std::to_string
|
||||
@@ -672,16 +672,20 @@ C++11 introduces new smart(er) pointers: `std::unique_ptr`, `std::shared_ptr`, `
|
||||
`std::unique_ptr` is a non-copyable, movable smart pointer that properly manages arrays and STL containers. **Note: Prefer using the `std::make_X` helper functions as opposed to using constructors. See the sections for [std::make_unique](#stdmake_unique) and [std::make_shared](#stdmake_shared).**
|
||||
```c++
|
||||
std::unique_ptr<Foo> p1 { new Foo{} }; // `p1` owns `Foo`
|
||||
if (p1) p1->bar();
|
||||
if (p1) {
|
||||
p1->bar();
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_ptr<Foo> p2 { std::move(p1) }; // Now `p2` owns `Foo`
|
||||
std::unique_ptr<Foo> p2 {std::move(p1)}; // Now `p2` owns `Foo`
|
||||
f(*p2);
|
||||
|
||||
p1 = std::move(p2); // Ownership returns to `p1` -- `p2` gets destroyed
|
||||
}
|
||||
|
||||
if (p1) p1->bar();
|
||||
if (p1) {
|
||||
p1->bar();
|
||||
}
|
||||
// `Foo` instance is destroyed when `p1` goes out of scope
|
||||
```
|
||||
|
||||
@@ -699,7 +703,7 @@ void baz(std::shared_ptr<T> t) {
|
||||
// Do something with `t`...
|
||||
}
|
||||
|
||||
std::shared_ptr<T> p1 { new T{} };
|
||||
std::shared_ptr<T> p1 {new T{}};
|
||||
// Perhaps these take place in another threads?
|
||||
foo(p1);
|
||||
bar(p1);
|
||||
@@ -714,8 +718,7 @@ start = std::chrono::steady_clock::now();
|
||||
// Some computations...
|
||||
end = std::chrono::steady_clock::now();
|
||||
|
||||
std::chrono::duration<double> elapsed_seconds = end-start;
|
||||
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
elapsed_seconds.count(); // t number of seconds, represented as a `double`
|
||||
```
|
||||
|
||||
@@ -762,7 +765,7 @@ These containers maintain average constant-time complexity for search, insert, a
|
||||
* Prevents code repetition when specifying the underlying type the pointer shall hold.
|
||||
* It provides exception-safety. Suppose we were calling a function `foo` like so:
|
||||
```c++
|
||||
foo(std::shared_ptr<T>{ new T{} }, function_that_throws(), std::shared_ptr<T>{ new T{} });
|
||||
foo(std::shared_ptr<T>{new T{}}, function_that_throws(), std::shared_ptr<T>{new T{}});
|
||||
```
|
||||
The compiler is free to call `new T{}`, then `function_that_throws()`, and so on... Since we have allocated data on the heap in the first construction of a `T`, we have introduced a leak here. With `std::make_shared`, we are given exception-safety:
|
||||
```c++
|
||||
@@ -785,10 +788,10 @@ The first parameter is the policy which can be:
|
||||
1. `std::launch::async` Run the callable object on a new thread.
|
||||
1. `std::launch::deferred` Perform lazy evaluation on the current thread.
|
||||
|
||||
```
|
||||
```c++
|
||||
int foo() {
|
||||
/* Do something here, then return the result. */
|
||||
return 1000;
|
||||
/* Do something here, then return the result. */
|
||||
return 1000;
|
||||
}
|
||||
|
||||
auto handle = std::async(std::launch::async, foo); // create an async task
|
||||
|
||||
Reference in New Issue
Block a user