cppreference.com日記

関数Tryブロック

以下のようなコードがかけちゃうらしい.
マジか,tryそんなとこに書けたんか

int main() try {}
catch (...){}

main関数

いわゆるエントリポイント,main関数

std::min_element, std::max_element

実行ポリシー(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

const auto [min, max] = std::minmax_element(begin(v), end(v));

std::enable_shared_from_this

クラス内でthisshared_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::promiseset_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!=()が適切に定義されていれば自作クラスでも使える.

注意点

範囲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のページのサンプルコードは説明が少なくてわかりにくいので,以下解説

属性構文(C++11)

ソースコードに関する追加の情報をコンパイラに伝えるための構文
主に,コンパイラの最適化のためと警告抑制のために使われる印象
型などとは違い,ユーザが属性を定義することは出来ない.

現在存在するのは以下

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に記述するとコンパイルエラーとなる

Coming Soon

0 Comments for this cheatsheet. Write yours!