2020-10-03
術語
Widget&& w
:rvalue reference 型別的變數本身是 lvalue...
(原始碼)從 expr
推導出 P
與 T
P
為 T&
/T*
expr
具參考則忽略expr
含 const
(而 P
本身沒註明的話),T
則推導出 const
。P
為 T&&
P
為 T
(call by value)
expr
具參考同樣忽略expr
含 const
也忽略 (volatile
也忽略)const
;指標本身 → 忽略 const
(因為複製了)f(T)
→ char *
(無法宣告真正的陣列做為參數)f(T&)
→ char (&)[16]
(卻能有真正陣列的參考)std::array
取代)auto
扮演 T
的角色。auto x = { 27 }
並非 int x = { 27 }
{}
~= std::initializer_list
,便推導成 std::initializer_list<int>
。auto
,template <typename T> void f(T param);
無法自 f({ 1, 2, 3 })
推導。auto f() { return { 1, 2, 3 }; }
無法推導auto f = [](auto& param) { … }; f({ 1, 2, 3 });
無法推導decltype
auto f(Container& c, Index i) -> decltype(c[i])
(trailing return type 語法,不是推導。)auto
套用 T
規則而非 T&
) → 以 decltype(auto)
解決Container
傳入 rvalue? T&&
(universal reference) + std::forward
(??!)decltype(x)
是 int
;decltype((x))
是 int&
。template<typename T> class TypeDetect;
小技巧typeid(x).name()
(不一定可靠)
auto
std::iterator_traits<It>::value_type
迭代器)auto
std::function
不同於 closure
unsigned size = v.size()
→ 在 64-bit 被截斷const pair<string, int>&
(但其實是 const string
) → 自動轉型、效能減損auto
+ proxyauto
碰到 proxy 會出問題 (如 vector<bool>::operator[]
)static_cast
。()
/{}
foo x(0);
ctorfoo x = 0;
ctor (implicit),但常被誤認為 assign。foo x{ 0 };
通用初始化foo x = { 0 };
亦同{}
~= std::initializer_list
auto x{ 0 }
→ initializer_list
initializer_list
ctor
initializer_list
foo x{}
!?
std::initializer_list
呢?? → foo x({})
或 foo x{{}}
()
優先 或 {}
優先nullptr
0
是 int
不是指標;NULL
也不是指標。void*
,但反方向不行,故不將 NULL
定義為 (void*)0
。nullptr
的型別是 std::nullptr_t
0
或 NULL
為引數而推導錯誤using x = y
my_struct<T>::type
typename
<type_traits>
enum class
{}
產生 scope → 但 enum
卻不會enum class
產生 scope,使用必須注明 (color::red
)。enum
能隱式轉換 → 並不好enum class
必須顯式轉換enum class
型別預設是 int
enum class color : uint32_t {…}
enum
亦可underlying_type_t
的例子:std::get<1>
搭配 enum class
使用= delete
basic_io
為例)private
且 不予實作public
並 = delete
private
而無法呼叫? 要有明確錯誤訊息,故 public
。好處都有啥?
override
virtual
override
→ 覆寫失敗必定報錯
const_iterator
const_iterator
並不堪用.cbegin()
/.cend()
便於 non-const 容器取得 const_iterator
begin()
/end()
便於泛型最適
cbegin()
、rbegin()
cbegin()
的方式noexcept
push_back
若 搬移 會拋出例外,則改用 複製。noexcept
) (例外安全 copy-and-swap 技巧)constexpr
不必再用 template 寫艱深的 meta-programing 啦~
mutex
、atomic
好棒棒!!class Polynomial
為例
mutable
成員)std::mutex
(std::lock_guard
)std::atomic
(成本較低,單一變數適用,不可用於多步操作)Polynomial
無法複製auto_ptr
已由 unique_ptr
取代unique_ptr
unique_ptr
雖支援 陣列,但無 operator[]
可用 → 還是用 STL 容器shared_ptr
shared_ptr
unique_ptr
) → 方便 置於容器、相互賦值、參數傳遞make_shared
unique_ptr
取得shared_ptr
兩次 → 兩份計數、釋放兩次 (未定義行為)
make_shared
this
的場合 → 改用 enable_shared_from_this
make_shared
在建立時塞入 control block 訊息。shared_ptr
建立。unique_ptr
升級,但無法回頭。operator []
很不方便weak_ptr
shared_ptr
而生
.expired()
檢查是否 dangling.lock()
取得 shared_ptr
(dangling 則 nullptr
)weak_ptr
呼叫 shared_ptr
之 ctor (dangling 則 throw bad_weak_ptr
)make_xxx
(C++14 才有 make_unique
)
make_shared
時,同時俱備 例外安全 與 效能 的辦法。
std::move()
uniqur_ptr
new
/delete
)unique_ptr
取代 new
/delete
.h
只有宣告;.cpp
再定義 (可用 = default
)。share_ptr
沒有上述的麻煩;但 unique_ptr
(單一所有權) 仍較適當。unique_ptr
、thread
)Widget&& w
:rvalue reference 型別的 w
,本身也是 lvalue。std::move
& std::forward
std::move
無條件轉型 – 成為 rvalue (稱 rvalue_cast
也許較貼切)
const
則無法搬移
std::move
後 const Foo
成為 const Foo&&
Foo&& f
無法接受 (不可放寬限制) → 無法搬移const Foo& f
可以接受 → 改以複製std::forward
有條件轉型 – 僅 rvalue 轉為 rvalue
std::forward
可完全取代 std::move
,但會看不懂你想幹嘛。T&&
幾乎可以參照任何東西 (lvalue、rvalue、…),筆者稱之 universal reference。template <typename T> void f(T&& x);
auto&& x = value;
const
的話void f(vector<T>&& x);
template <typename T> class { void f(T&& x); };
template <typename... Args> void f(Args&&... args);
[](auto&&... params) { … }
std::move
轉發std::forward
麻煩、易錯、又很怪std::forward
轉發std::move
搬移了,其值將未定義。w.f("text");
增加成本std::move
(否則仍需使用該 rvalue 的話…)std::move
std::forward
std::move
int
/short
、有無 const
),就會被 universal reference 吃掉。universal reference + overload 會出事 → 條款 26
const Type& value
std::move
→ 條款 41std::is_integral<…>()
(std::remove_reference_t<T>()
)std::true_type
和 std::false_type
std::enable_if
!std::is_same<Person, T>::value
型別不是 Person
std::decay<T>::value
也不是 Person
的 const 或 referenceT
若是 Person
的任何衍生類別也都不行int
版本concept
/requires
static_assert
std::forward
藉此達成
Type& &&
→ Type&
Type &&
→ Type&&
auto
宣告再傳入NULL
作為指標 → 改用 nullptr
啦笨蛋!!這些以前就都能做到,只是 lambda 更加易寫易讀。
術語
[x = y]{…}
std::bind
用到再查書 :pauto&&
參數 用 std::forward<decltype(x)>
auto
,猶如 template operator()
。auto&& x
)std::forward<???>
的 ???
要填什麼? (沒 T
可填)
decltype(x)
取代,效果相同。auto&&...
)std::bind1st
/std::bind2nd
std::bind
(since 2005)_1
、_2
)now()
成為 呼叫 std::bind
時間,而非呼叫 函式物件 時間。 → 解法:需雙層 bindstd::ref()
std::bind
std::future
與 std::shared_future
,但通常不特意區分。std::async
> std::thread
int work()
函式
std::thread t(work);
auto f = std::async(work);
std::thread
: 以物件管理 software thread 之 handle,可能為 null。std::thread
的麻煩
std::system_error
(即便 int work() noexcept
)std::async
比較好
.get()
)std::launch::deferred
std::thread
的情況很少:⑴ ⑵ ⑶ 略std::lanuch::async
std::async
不一定會平行處理
std::lanuch::async
: 於另個執行緒平行執行std::launch:deferred
: 延遲至 get
或 wait
時才執行 (否則不執行)thread_local
)wait_for
或 wait_until
記得判斷 std::future_status::defered
(以 wait_for(0s)
)real_async
。std::thread
解構時必須 unjoinablestd::thread
的範例
10'000'000
增加可讀性std::thread
解構時,卻仍 joinable? → 標準委員會:應終止程式
thread_raii
決定 join 或 detach
std::thread
std::thread
成員放最後 (最晚開始;最早離開).get()
存取底層.join()
std::thread
std::thread
↔ 底層執行緒 💬 是說 條款 37 吧?std::future
↔ 所指派任務 (未延遲??)std::promise
.get_future()
std::future
.share()
→ std::shared_future
shared_future
該歸誰?std::async
所建立std::lanuch::async
packaged_task
並以 std::thread
執行void
future目標 通知其他執行緒此事件發生
方法㈠ 條件變數 (condition variable)
.notify_one()
或 .notify_all()
std::unique_lock
) 💬 以免與其他 接收端 競爭?.wait()
/.wait_for()
/.wait_until()
方法㈡ 布林旗標 (boolean flag)
std::atomic<bool> flag(false);
flag = true;
while (!flag);
方法㈢ 混合㈠與㈡
bool
即可。方法㈣ promise & future
void
即可:std::promise<void> p;
p.set_value();
p.get_future().wait();
.set_value()
)
.get_future().share()
std::shared_future
.set_value()
,理當給 future 拋出例外。std::atomic
與 volatile
std::atomic
++i
/--i
:讀取-修改-寫入 (read-modify-write, RMW) 可保證 atomicstd::cout << i
不保證整個指令都 atomic (僅 讀取 為 atomic).load()
與 .store()
,但兩者不合併為 atomic。volatile
volatile int x;
auto y = x;
– 型別推導捨去 volatile
視情況 考慮 使用
目的 為了效率
方法㈠ 對 lvalue 複製;對 rvalue 搬移。
方法㈡ universal reference
std::string
的型別皆有作用 → 條款 25方法㈢ 違反守則或許合理:call by value
std::move()
成本
方法 | 場合 | caller | callee |
---|---|---|---|
㈠ | lvalue | 參考 | 複製 |
㈠ | rvalue | 參考 | 搬移 |
㈡ | lvalue | 參考 | 複製 |
㈡ | rvalue | 參考 | 搬移 |
㈢ | lvalue | 複製 | 搬移 |
㈢ | rvalue | 搬移 | 搬移 |
討論
shared_ptr
恐資源洩漏 → 可先建立暫時物件再 move 進容器