関数Tryブロック
以下のようなコードがかけちゃうらしい.
マジか,tryそんなとこに書けたんか
int main() try {}
catch (...){}
main関数
いわゆるエントリポイント,main
関数
main
関数はint
型だが,return文はなくてもOKauto main()
という書き方はダメらしいreturn N
はstd::exit(N)
と同じ意味を持つmain
関数が関数try
ブロックになっていてもmain関数の外で定義された(しかし,main
関数の終了と同時に破棄される)静的オブジェクトのデストラクタで投げられた例外はキャッチできない
std::min_element, std::max_element
- 最低限,探索範囲を[first, last)で指定できる.
- 比較には
<
演算子を使用(max_element
は>
演算子を使うわけではない) <
演算子の代わりに比較関数を指定できる.- 比較関数は
cmp(new_element, best_element)
という形式で使われる - ⇒内容によって意図的に弾きたい場合は,第一引数の内容を参照して弾けば良い(min_elementならfalse,max_elementならtrueを返せば弾ける)
- 比較関数は
- 実行ポリシーを設定できるらしい(知らんかった).指定箇所は探索範囲の前.
- 詳しくは実行ポリシー参照とのこと
実行ポリシー(C++17)
stlにはたくさんのアルゴリズム関数が存在するが,それらの第一引数にアルゴリズムの処理を実行する上での制約を「実行ポリシー」として与えることができるらしい. 「実行ポリシー」は以下のような種類が存在する
実行ポリシー | 説明 |
---|---|
seq |
順番に処理を実行する必要がある.当然並列化も出来ない |
par |
マルチスレッド化による並列化を許可する |
par_unseq |
マルチスレッド化・ベクトル化を許可する |
unseq |
ベクトル化を許可する(C++20) |
今までのアルゴリズム関数はseq
で実行されるのが当然だった.
プログラマにから与えられた関数オブジェクトの処理はどういう処理なのかをアルゴリズム関数は知らないのだからそりゃそうだ.
でも,そのプログラマが処理についてアルゴリズム関数にヒントを与えられるのがこの機能というわけだ.
もしかしなくても「OpenMPを使ったfor文の並列化は簡単でいいなぁ」と言っていた時代は終わったのかもしれない
std::for_each(std::execution::par, a.begin(), b.end(), [&](int x) {
std::cout << x << std::endl;
});
std::minmax_element
- コンテナの範囲を渡すと,最大値と最小値をペアにして返してくれる
- 例によって二項比較関数は
<
演算子相当のものを定義して渡せば良い - C++17からは実行ポリシーも指定可能
std::make_pair(std::min_element(), std::max_element())
よりも効率が良い- 一番大きい/小さい要素が複数ある場合,↑は最初の要素を返し,本アルゴリズムは最後の要素を返す
const auto [min, max] = std::minmax_element(begin(v), end(v));
std::enable_shared_from_this
クラス内でthis
をshared_ptr
にしたい場合が往々にしてある.(全て自分のコードの場合では設計が悪い場合が多いが,外部ライブラリを使用しているとそうするしか無い場合もありがち)
そういう時に,std::shared_ptr<T>(this)
などとやってしまうと,this
は2回以上破棄されてしまいやすい.
そんな時に使うのが,これstd::enable_shared_from_this
. 使い方はshared_ptr
化したいクラスでstd::enable_shared_from_this
をpublic継承し,shared_from_this()
を呼ぶだけ.
しかし,本機能を使っているクラスは必ずshared_ptrで管理されている必要があるので注意が必要.そうでない場合の動作は未定義.
class A : public std::enable_shared_from_this {
A(){
auto shared_ptr_this = shared_from_this();
}
};
std::future
非同期処理を行うためのクラス.このクラスでは今は無いが,「未来」に手に入るであろうデータを取り扱う.
データが手に入ったかどうかは,valid
関数で調べることができ,get
関数で取り出せる.
また,手に入るまで待機するwait
関数も何種類か用意されている.
関数名 | 説明 |
---|---|
wait |
処理完了まで待つ |
wait_for(<duration>) |
処理完了まで待つ(指定時間でタイムアウト) |
wait_until(<time_point>) |
処理完了まで待つ(指定時間になったらタイムアウト) |
std::promise
std::future
に出来上がったデータを受け渡す役割を持ったクラス
使い方は以下のような感じ
std::promise<int> p;
std::future<int> f = p.get_future();
p.set_value(1);
std::cout << f.get() << std::endl;
std::primise
は,データを渡す相手に「今はまだ空だけど,将来ここにデータを送るからね」と言ってstd::future
を渡す.
後日,データを見つけたstd::promiseは
set_value関数でデータを送り,
std::future側では
get`関数で受けとれる.
委譲コンストラクタ(C++11)
C++11からの機能.複数のコンストラクタ内で共通の処理を初期化処理で行える.
(コンストラクタの本体にメンバ関数を呼び出す方法もあるが,メンバ関数は初期化が完了した後にしか呼び出せない共通処理なので,ベタ書きしたり委譲コンストラクタを使う場合に比べてパフォーマンスで劣る)
class X {
public:
X(int i) { std::cout << i << std::endl; }
X() : X(42) {}
};
初期化子リスト(C++11)
以下のような,波括弧による初期化を可能とする’
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 {1, 2, 3};
初期化子リストを使うには,std::initilizer_list
を引数とするコンストラクタが必要’
class MyVector {
std::vector<int> data_;
public:
MyVector(std::initializer_list<int> init)
: data_(init.begin(), init.end()) {}};
一様初期化(C++11)
std::initilizer_list
以外のコンストラクタも波括弧を使った初期化ができる機能
情報が足りていなかったりすると,std::initializer_list
型として推論されてしまったりするので注意.
struct X {
X(int, double, std::string) {}
};
X createX(){return {1, 3.14, "hello"}; }
std::initializer_list
のコンストラクタとそれ以外のコンストラクタの両方で受け取れるような波括弧リストを渡す時,前者が呼び出される.
但し,空の初期化子リストを渡す場合はデフォルトコンストラクタが優先される.
インライン名前空間(C++11)
inline namespace my_namespace{}
省略可能な名前空間を定義できる.
上位の名前空間をusing
するだけで下位のinline名前空間の機能が使えるようになるので,本来なら複数のusing
文が必要な場合も,1つで事足りる.
以下のように,複数バージョンの関数がそれぞれのバージョンの名前空間にある時,1つだけinline
名前空間で定義することでデフォルトのバージョンを表現することが出来る.
また,デフォルトバージョンを変更する場合もinline
名前空間を切り替えるだけで実現できる.
namespace api {
inline namespace v1 {
void f(){}
}
namespace v2 {
void f(){}
}
}
入れ子名前空間の定義(C++17)
入れ子上になった名前空間を一度に定義することが可能になる
C++14まで
namespace aa{
namespace bb{
void f(){}
}
}
C++17から
namespace aa::bb{
void f(){}
}
入れ子名前空間でのインライン名前空間(C++20)
C++11からのインライン名前空間とC++17からの入れ子名前空間の併用は出来なかったが,C++20から出来るようになった
C++17
namespace aa{
inline namespace bb{
void f(){}
}
}
C++20
namespace aa::inline bb{
void f(){}
}
範囲for文(C++11)
for文を簡潔に書くことが出来る機能.
for ( auto element : elements ) std::cout << element << std::endl;
↑は↓のように展開される(C++17以降や,配列を扱う時は展開され方が異なる)
auto && __range = elements;
for (auto __begin = __range.begin(), __end = __range.end(); __begin != __end; ++__begin) {
auto element = *__begin;
std::cout << element << std::endl;
}
↑から分かるように,begin()
,end()
,operator++()
, operator*()
, operator!=()
が適切に定義されていれば自作クラスでも使える.
注意点
- 要素をeraseしてはいけない(バグる)
- インデックスの取得とは相性が悪い(出来ないこともない:別ページ参照)
- 範囲として渡した配列などが,範囲for文実行中に寿命が切れないように注意
範囲forループの制限緩和(C++17)
範囲for文を適用するコンテナのbegin()
関数とend()
関数の戻り値の型が一致していなくても良くなった.
正直使う機会は少ないが,番兵法アルゴリズムを使いたいときなどは便利に使えるらしい.
使いたいから緩和したというより,制限する意味がなかったから緩和したという方が正しそう?
範囲for文がカスタマイゼーションポイントを見つけるルールを緩和
範囲for文を展開する時にbegin()
とend()
を探すルールが変更された.(C++11まで遡って変更されていて,gcc8,clang8以降で適用されている)
適用前イメージ
if ( beginメンバ関数が存在 OR endメンバ関数が存在 ){
beginメンバ関数を使用
}else{
非メンバbegin/end関数を探す&使う
}
適用後イメージ
if ( beginメンバ関数が存在 AND endメンバ関数が存在 ){
begin/endメンバ関数を使用
}else{
非メンバbegin/end関数を探す&使う
}
cppref.jpのページのサンプルコードは説明が少なくてわかりにくいので,以下解説
- そもそも
std_stringstream
にはbegin()
とend()
が存在しない - しかし,
std_stringstream
が継承しているstd::ios_base
にはstd::ios_base::end
(関数ではない)が存在する std::ios_base::end
が範囲for文に必要なend
として認識される- 適用前のルールだと,
begin
の存在を確認することなくメンバ関数で範囲forの展開をしてしまう - 適用後のルールでは,
begin
の存在も確認を行うため,std::ios_base::end
は範囲forの展開に使われない - 適用後のルールでは結局非メンバの
begin
とend
が使われて問題なくコンパイルできる
属性構文(C++11)
ソースコードに関する追加の情報をコンパイラに伝えるための構文
主に,コンパイラの最適化のためと警告抑制のために使われる印象
型などとは違い,ユーザが属性を定義することは出来ない.
現在存在するのは以下
- noreturn(C++11)
- carries_dependency(C++11)
- deprecated(C++14)
- maybe_unused(C++17)
- nodicard(C++17)
- fallthrough(c++17)
- no_unique_adress(c++20)
- likely, unlikely (c++20)
noreturn属性(C++11)
⇒警告抑制タイプの属性:「関数が返らないパスが存在する」という警告を抑制
⇒最適化促進タイプの属性:制約を増して最適化に寄与
[[noreturn]] void report_error(){
throw std::runtime_error("error");
}
関数が決して返らない(=必ず例外が投げられる?)ことをコンパイラに伝える
この属性がついた関数が返る場合の動作は未定義
carries_dependency属性(C++11)
TBD
TODO:
deprecated属性(C++14)
[[deprecated("please use new_func() function")]] void old_func() {}
非推奨であることを示す属性.使うとコンパイル時に警告を発する
警告で出すメッセージも指定できるので,代わりに使って欲しい機能を示すとより親切
この属性が使える場所は以下の通り
- クラス
- 型の別名
- 変数
- 非静的メンバ変数
- 関数
- 列挙型
- テンプレートの特殊化
maybe_unused属性(C++17)
⇒警告抑制タイプの属性:意図して使ってない要素に対してコンパイラに文句を言わせない
主に,各種宣言部分で色々使える
class [[maybe_unused]] X;
using integer [[maybe_unused]] = int;
[[maybe_unused]] typedef int integer;
[[maybe_unused]] void f();
template <class T>
[[maybe_unused]] inline void f();
enum class [[maybe_unused]] E {
A [[maybe_unused]],
B
};
関数の引数の場合,maybe_unusedを指定せずとも,引数名を定義しないことで警告を抑制できる.
void func1([[maybe_unused]]int unused_arg){}
void func2(int){}
nodiscard属性(C++17)
「関数の戻り値を破棄してはいけない」という情報をコンパイラに伝え,エラー処理など本来無視してはならない戻り値の意図しない無視をプログラマに警告として伝えるための属性である.
関数に使用した場合,その返り値を無視すると警告を発するようになる. クラス,構造体,列挙型の宣言に対して指定した場合,その型の返り値を無視すると警告を発する.
struct [[nodiscard]] error_info{};
[[nodiscard]] int func(){return 0;}
C++20では更に以下の改良が加わっている
fallthrough属性(C++17)
switch-case文においてcaseの次にbreak文を置かないことで連続の複数のcaseに渡ってプログラムが実行されること(fallthrough)は,
しばしば悪用されるが,プログラマが見落としやすくバグにもつながりやすい.そのため,コンパイラはフォールスルーを検知すると警告を出す.
この[[fallthrough]]
属性はフォールスルーが意図的であることを伝えると同時に,プログラマにとってもフォールスルーを見つけやすくするものである.
switch (N) {
case 1:
// do something
[[fallthrough]]
case 2:
break;
default:
}
なお,最後のcaseやdefaultに記述するとコンパイルエラーとなる
0 Comments for this cheatsheet. Write yours!