2020年3月にメモしたことまとめ
先月クソ遅れたが、今月は少し早くできた。リマインダーが鳴ってから記事を書き始めるのでは遅いことを学んだ。
15日
18日
20日
雑感
数が少ない事の言い訳はしない。大体は ARK:Survival Evolved で遊んでいた自分が悪い。
それはそれとして、ノートで取れるものはノートで取ってしまうし、PDF で落とせるものは落としておくので授業期間などの全く知らないものを詰め込まれる期間の方がこういった体系的でない知識は得やすいのだろうか。ただし模索中のものは文章化できない。なんとも文章力のない人間である事だろう。
gcc の HEAD をビルドするときの備忘録
Hyper-V 上の仮想環境を消して WSL2 に移行しようとしたら色々環境構築を忘れていたのでメモ。
Wandbox のビルドスクリプト を非常に参考にしている (というかほぼ丸パクリ)。備忘録なので許して。
環境は Ubuntu on WSL2 だが、apt を適当なパッケージマネージャに置き換えれば他のディストリでも問題ない気がする。
前準備
適当なディレクトリを作って移動する。今回は~/gcc-head/
。
mkdir gcc-head cd gcc-head
ソースを持ってくる
github のミラーからsource
ディレクトリにソースを持ってくる。
このとき--depth 1
をつけると最新コミットだけ持ってくるので clone が速くなる。
git clone --depth 1 https://github.com/gcc-mirror/gcc.git source
必要なもののインストール
gcc にはビルドに必要なツールを一括で入れる方法が用意されている…と思いきや、なぜか flex だけインストールされなかったので追加で apt install する。
cd source ./contrib/download_prerequisites sudo apt install flex cd ..
contrib/download_prerequisites
が (名前の通り) 必要なものを一括で入れてくれるものなのだが、最初にcd source
しておかないと動いてくれないので注意。
終わったらsource
から出ている。
スクリプトを書く
後はほぼビルドするだけなのだが、コマンドが長いので適当なスクリプトファイルに書いてから実行したほうが良いと思う。上記の wandbox のスクリプトを参考にしつつ以下のようなスクリプトを書いてbuilder.sh
として保存。
PREFIX="$HOME/bin" mkdir build cd build ../source/configure \ --prefix=$PREFIX \ --enable-languages=c,c++ \ --disable-multilib \ --without-ppl \ --without-cloog-ppl \ --enable-checking=release \ --disable-nls \ --enable-lto \ LDFLAGS="-Wl,-rpath,$PREFIX/lib,-rpath,$PREFIX/lib64,-rpath,$PREFIx/lib32" make -j2 make install
環境変数 PREFIX
で指定したディレクトリ以下にインストールされるので/usr/local
なりに適当に変えると良さそう。オプションの雑な意味としては以下。gcc のページ を見るとちゃんとした解説があるのでそちらを見たほうが良い。
オプション | 説明 |
---|---|
--enable-languages=c,c++ | C と C++ のみ有効にする |
--disable-multilib | 64bit 向けのバイナリのみを吐けるようにする *1 |
--without-ppl | PPL という古の幾何計算ライブラリっぽいのを使わなくなる |
--without-cloog-ppl | ほぼ同上 |
--enable-checking=release | ビルドしたバイナリに余計なデバッグ機能を含めない |
--disable-nls | 診断メッセージの言語としてアメリカ英語以外を使えなくなる |
--enable-lto | リンク時の最適化を有効化する |
LDFLAGS=... | リンク用のディレクトリを指定する。-Wl は以降の , を半角スペースに置換してくれる |
インストールする
. builder.sh
としていっぱい待つと$PREFIX/bin
以下にバイナリができている。
*1:32bit 環境をターゲットに開発したいならそれ用の libc などを入れておく必要がある
2020年2月にメモしたことまとめ
先月はブログに投稿したことをすっかり忘れていた。3月も16日になってから気づき慌ててこの記事を書いている。
さすがに三日坊主以下なので、3月分は4月1日に投稿できるようにリマインダーをセットした。
10日
12日
16日
24日
雑感
2月は中国語を中心にやっていたら前半がすぎ、後半は本を読んではいたもののメモがほぼない。メモを作れるほど構造的に読書出来ていなかったのだろうか。これからはもう少し注意して読書します。
2020年1月に知ったことまとめ
今月の半ばから、学んだ/考えたことのうち後で参照しそうな事は OneNote にまとめるようにした。
試験期間が重なったのもあり、毎日やるというわけには全く行かなかったが、継続するための試みとして今月分をブログに上げておく。
2月中も僕が真面目なら来月にも上げているかもしれない。
15日(水)
16日(木)
18日(土)
21日(火)
31日(金)
雑感
21日の次が31日でかなり空いている(定期試験の影響)。レポートや授業ノートもあるのでここに書いてある分が学んだ内容全てではないが、せっかく試験に向けて勉強しているのに、そのエッセンスをまとめる時間が無いというのは勿体無い気もする。
長々と文章を書くと下手に凝り出してしまって頓挫するので、こういう形でアウトプットしていくのはありかもしれない。間違いに気づいたら教えてくれると泣いて喜びます。
JOI 2017-2018 本選参加記
自戒の意をこめて問題に関するものだけ書きます。
問題は https://www.ioi-jp.org/joi/2017/2018-ho/index.html で見てください。
1 問目
やるだけ。
適当に不連続な部分の長さをカウントして、短い方から使う。
#include <iostream> #include <vector> #include <cstdint> #include <algorithm> int main() { int n, k; std::cin >> n >> k; std::vector<int64_t> times(n); for (auto&& e : times) std::cin >> e; std::sort(times.begin(), times.end()); int block = n; std::vector<int64_t> blanks; blanks.reserve(n); for (int i = 1; i < n; ++i) { if (times[i - 1] != times[i] - 1) blanks.push_back(times[i] - times[i - 1] - 1); else --block; } std::sort(blanks.begin(), blanks.end()); block -= k; int64_t ans = n; for (int i = 0; i < block; ++i) ans += blanks[i]; std::cout << ans << std::endl; }
2 問目
大きさに関しては最大最小のみが重要なのでとりあえず大きさでソートしてみる。
するとソートした中では連続している美術品を展示する方が良いのがわかる。
また、隣合う美術品の大きさの差を diff
とすると例えば入力例 1 では次のようになる。
水色の枠の中にあるのは美術品の価値。
今回は大きさでソートすると [(2, 3), (4, 5), (11, 2)]
みたいになるので図のような感じになる。
すると求めたい最大値は「ある美術品と美術品の間の価値の合計からその間にある diff を全て引いた数の最大値」と言い換えられる。
例えば入力例 1 では最初の美術品の価値が 3、次の美術品との diff が 2、二つ目の美術品の価値が 5 で、これを考えた 3 + 5 - 2 = 6
が最良になる。
そう考えるとこの問題は「数列(もどき)の区間和を最大化する」問題に帰着できる。
一瞬セグ木を書きそうになるが、グッと堪えると別に配列を舐めながら走査すれば求まることに気づく。
そんな感じで思考して AC。
#include <iostream> #include <cstdint> #include <vector> #include <algorithm> int main() { int64_t n; std::cin >> n; std::vector<std::pair<int64_t, int64_t>> ab(n); for (auto&& e : ab) std::cin >> e.first >> e.second; std::sort(ab.begin(), ab.end()); int64_t ans = 0; int64_t sum = 0, min = 0; for (int i = 0; i < n; ++i) { sum += ab[i].second; ans = std::max(ans, sum - std::min(min, sum)); if (i < n - 1) sum -= ab[i + 1].first - ab[i].first; min = std::min(min, sum); } std::cout << ans << std::endl; }
3 問目
最初団子が刺さる向きが規定されていないと思ってしまいグラフ問題か?となるが、問題をちゃんと読むと刺さる向きは左→右か上→下と書いてある。
考えると左肩下がりの斜め向きの RGW だけ考えればそれ以外は干渉しない気がするので、斜め向きに DP して後で全部足す。提出。小課題 1 以外通らない。
コンテストが残り 1 時間のため「きっと解法が違うんだろう」と諦めて他の問題の部分点を狙おうとする。
これが結果的に大間違いだった事が後で分かる。解法は完全に正しくて実装がバグっていただけだったっぽい。
4 問目
満点解法は微塵も思いつかず、小課題 2 と 3 は分かったため書こうとする。Dijkstra の経路復元をバグらせて Ubuntu の仮想マシンがフリーズする。焦る。
バグを直したところでタイムアップになり、提出できずに終わる。
5 問目
問題だけ読んでデータ構造っぽいとか言ってた。大嘘だった。
総評
2 完 + 3 問目の部分点で 213 点。おそらく 30 位前後だと思う。春合宿には行けなかった。
敗因が明確すぎてとても悔しかった。去年部分点を取らなかったせいで春合宿を逃したのが、今年は部分点を取ろうとしたせいで春合宿を逃すという良い失敗の事例っぽい感じ。
相方がやる気を出し、僕もこれで終わりたくないので PCK には多分出ます。
1 ページで眺める C++17
この記事は C++AdC 2017 19 日目の記事です。
n 番煎じ過ぎてみなさんがやらなかったやつをやります。
色々と cpprefjp と 江添さんのやつ を参考にしています。記述が被っている場所すらあるかもしれません。ごめんなさい。
言語機能
コードでの説明が必要なやつから紹介。だんだん雑になるかも。
構造化束縛(structured bindings)
今まで std::tuple
を扱うのは面倒で、例えば
std::tuple<int, double, std::string> tup = f(); // auto tup = f(); でも良い auto x = std::get<0>(tup); auto d = std::get<1>(tup); auto s = std::get<2>(tup);
または
int x; double d; std::string s; std::tie(x, d, s) = f();
みたいに書く必要があった。これは微妙に使う気が起きない。C++17 からはこう書ける
auto [x, d, s] = f();
これなら使う気がぐんぐん湧いてくる。
これは tuple に限った機能ではなく、配列、条件を満たすクラスなどにも使える
auto [a1, a2, a3] = { 5, 1, 3 }; struct Pos { int x = 10, y = 10, z = 10; }; auto [x, y, z] = Pos();
便利だね。
ただ
int a = 5, b = 3; [a, b] = std::minmax(a, b);
みたいなことは出来ない。あくまで構造化束縛は変数の定義/初期化のみの構文に留まっている。
こういう便利そうな機能が微妙な仕様で終わってるあたり C++ らしくていとあはれなり(社会性フィルター)。
条件式書くときの構文変更
書くたびになんか微妙な気がするこういうコード。
auto result = f(); if (result.isOK()) { // 処理 }
C++17 からはこう書ける
if (auto result = f(); result.isOK()) { // 処理 }
この構文は if
だけでなく、while
や switch
などでも使える。
template 引数推論
初期化時にちょっと嫌になるこういうやつ
std::vector<std::vector<int>> vec(m, std::vector<int>(n, x));
C++17 からはこう書ける
std::vector vec(m, std::vector<int>(n, x));
これは std::vector
の template 引数が推論されるようになったから。
ある程度は自動で推論してくれるし、もし自動では出来ないような推論をさせたい場合
template <typename T> struct Vec { std::vector<T> vec; // コンテナが渡されたとき、それを std::vector に持ち替えたい template <typename C> Vec(const C& rhv) : vec(rhv.begin(), rhv.end()) {} }; template <typename C> Vec(const C&) -> Vec<typename C::value_type>; // コンテナの value_type を T とする
のように Hoge(引数) -> Hoge<推論する型>
という構文で推論させることができる。
これを利用して明示的に文字数を指定しないでコンパイル時文字列を扱えるようになったりするのを 組んでみた けど、割りと面白かった。
RVO の保証
今までこんなコードを書いた時、返り値のオブジェクトがコピーされる可能性があった。
std::vector<int> f() { auto c = hoge(); // 何かしらのコンテナが帰ってくる return std::vector<int>(c.begin(), c.end()); // ここで返り値のコピーが発生する可能性がある }
なぜ「可能性」なのかというと、これをコピーしないようにする最適化、Return Value Optimization (RVO) が規格上認められていたからなのだが、C++17 からはこの関数 f の返り値を利用して値を初期化する場合。すなわち
auto v = f();
のようなユースケースの場合はこの最適化が強制されるようになった。
これにより、上記のコードで必ずコピーが発生しないようになる。
訂正: 記事公開時点では
std::vector<int> f() { std::vector<int> vec = hoge(); // なんか処理 return vec; }
のようなコードで RVO が保証されるかのような書き方をしていましたがこれは誤りでした。関数の返り値が prvalue
カテゴリでかつ変数の初期化に使われるときのみ RVO が保証されるようです。
ご指摘 していただいた銀天すばるさんに感謝。
template
template で任意の型のコンパイル時定数を渡したい人に朗報
template <auto X> struct Hoge { // 色々 };
のようなコードを書くことで
Hoge<30> a; Hoge<42ll> b; void f() { std::cout << "Hello, world!" << std::endl; } Hoge<f> c;
みたいなコードを書くことができる。局所的に役立ちそう。
constexpr if
あの C++ TMP を象徴する機能、SFINAE の地位を半分奪うかもしれない素晴らしい機能。
template < typename T, std::enable_if_t< std::is_same_v<std::decay_t<T>, int>, std::nullptr_t > = nullptr > void f(T) { std::cout << "T is int" << std::endl; } template < typename T, std::enable_if_t< !std::is_same_v<std::decay_t<T>, int> && std::is_integral_v<T>, std::nullptr_t > = nullptr > void f(T) { std::cout << "T is integer but not int" << std::endl; } template < typename T, std::enable_if_t< !std::is_integral_v<T>, std::nullptr_t > = nullptr > void f(T) { std::cout << "T is not integer" << std::endl; }
みたいなよくある地獄コード(実際はもうちょっとスマートに書けるかも)が、なんとこうなる。
template <typename T> void f(T) { if constexpr (std::is_same_v<std::decay_t<T>, int>) std::cout << "T is int" << std::endl; else if constexpr (std::is_integral_v<T>) std::cout << "T is integer but not int" << std::endl; else std::cout << "T is not integer" << std::endl; }
なんということでしょう。もうあのクソオーバーロード解決をしなくて良いのです。めでたしめでたし。
ところが一つ罠があり、この constexpr if
の操作は Two Phase Lookup の二段階目で行われるため、パースした段階で明らかにエラーである
template <typename T> void f() { if constexpr (false) static_assert(false); else std::cout << "fine" << std::endl; }
とかのコンパイルが通らない。この解決策としてはコードを
template <typename T> void f() { if constexpr (false) static_assert(std::is_integral_v<T> && false); else std::cout << "fine" << std::endl; }
こうすると static_assert
の中身が一応 T
に依存するので、コンパイルが通るようになる。前からある問題とは言えとても C++ らしいエラーの出し方でいとあはれなり。
畳み込み演算(Fold Expression)
今までこう書いてたやつが
template <typename T> auto sum(T x) { return x; } template <typename T, typename... Types> auto sum(T x, Types... args) { return x + sum(args...); }
こう書けるようになる
template <typename... Types> auto sum(Types... args) { return (... + args); }
これは (((args#0 + args#1) + args#2) + ...)
みたいに展開される(表記は江添さんが使ってるやつです)。
(args#0 + (args#1 + (args#2 + ...)))
みたいに展開したい場合
template <typename... Types> auto sum(Types... args) { return (args + ...); }
とすればいい。
注意点として、式全体を ()
でくくらないとエラーになる。
template <typename... Types> auto sum(Types... args) { return ... + args; // 括弧がないのでエラー! }
あと、畳み込む演算自体は結構自由にできる。こんなんでもいい。
template <typename... Types> auto sum(Types... args) { return (... + f(args).hoge()); }
例では +
演算子を使っているが演算子ならだいたい使える。
余談: これを悪用してみたいなぁとか考える人向けの某中3女子さんのツイートがこちら
C++17 fold expression に対応した比較式ユーティリティを書いてみた。 https://t.co/2uoV1cxDhw
— 狂える中3女子ボレロ村上/陶芸C++er (@bolero_MURAKAMI) 2017年10月28日
constexpr ラムダ
- コードの説明ないけど重要そうだから上の方入れた
- とうとう
constexpr
なラムダ式が作れるようになった - 特にキーワードも必要なく、単にコンパイル時に実行できそうなラムダはコンパイル時の文脈で使える
- つまり
constexpr
な変数とかstatic_assert
の条件式内でラムダ式が使える - 色々できそうだが、なんとSFINAEでの悪用を防ぐためにtemplate引数の中には書けないので注意
ラムダ式の *this キャプチャ
以下のコードには重大なバグがある
struct Hoge { int x; auto f() { return [this]() { return this->x++; }; } } int main() { std::function<int()> f; { Hoge hoge{5}; f = hoge.f(); std::cout << f() << std::endl; } std::cout << f() << std::endl; }
そう、最後のラムダ式内で this
をキャプチャしたはいいものの、最後の f()
の呼び出し時には hoge の寿命は切れている。つまりキャプチャしたはずの this
の中身が開放されているので見事未定義動作を踏み抜くことになる。
これを回避するため C++17 では this
の中身である *this
を直接キャプチャできるようになった。
struct Hoge { int x; auto f() { return [*this]() { return this->x++; }; // *this がコピーキャプチャされるので寿命も安心 } } int main() { std::function<int()> f; { Hoge hoge{5}; f = hoge.f(); std::cout << f() << std::endl; } std::cout << f() << std::endl; }
ネストされた名前空間定義の省略
いままで書いていた
namespace A { namespace B { namespace C { // ここに色々書く } } }
みたいな非常に微妙にアレなやつが
namespace A::B::C { // ここに色々書く }
と書けるようになった。嬉しい。
属性の追加
[[fallthrough]]
と [[nodiscard]]
、[[maybe_unused]]
が追加された。
[[fallthrough]]
は switch 文の中で用いて
switch (hoge) { case A: // なんか処理 [[fallthrough]] case B: // なんか処理 break; default: break; }
みたいにすると case A
の最後の部分で警告が出なくなる。
[[nodiscard]]
は関数の定義につけると、その関数の返り値が使われていないときに警告を出す。標準ライブラリでも malloc
などいくつかの低レベルのメモリ操作関数にこの指定がついた。
[[maybe_unused]]
は変数の定義につけるとその変数がその先使われていなくても警告を出さない。デバッグとかに便利かも。
一部演算子の評価順序の固定
int& f(int& x, int v) { std::cout << v; return x; } int x = 0; f(x, 2) = f(x, 1);
このようなコードを書いたとき、今までは演算子の右辺左辺の評価順序が未規定だったのでこの結果は 12
にも 21
にもなり得た。
C++17 からは例えば b = a
のとき a => b
の順に評価されるされるのが保証された。結果さっきのコードの実行結果は必ず 12
になる。
このようにして評価順序が固定される演算一覧はこんな感じ。全部 a => b
の順に評価される。
a.b
a->b
a->*b
b = a
b @= a // @ は任意の演算子
a[b]
a << b
a >> b
なお期待されていた f(a, b, c)
などの関数呼び出しにおける引数 a, b, c
の評価順序の固定はされなかった。未だに未規定です。
その他
コードで説明する感じでもない色々を箇条書きしていく。
noexcept 指定が関数の型の一部に
- 今まで
void f(int x)
とvoid g(int x) noexcept
は同じvoid(int)
型だった - C++17 からはこれが区別され、上の例でいえば
g
の型はvoid(int) noexcept
になる void(int) noexcept
からvoid(int)
には暗黙の変換がされるが、逆はされない- template 周りで既存のコードが動かなくなる可能性があるので注意
- こんなところに入れてるけど結構重要な変更
byte 型の追加
- 他の整数からの暗黙変換が不可能な 8bit 整数型、
byte
型が追加された - ストレージ用の型なのか、四則演算が定義されていない
inline 変数
inline Hoge x;
とかでinline
な変数が作れるようになった- ヘッダーオンリーライブラリの幅が広がるね
static_assert の記法追加
static_assert(cond, "")
がstatic_assert(cond)
と書けるようになった
using の可変長引数の展開が可能に
using Args::operator()...;
みたいな事ができます- というか今までできなかったのに気づいたときは驚いた
UTF-8 文字リテラル
u8'A'
で UTF-8 でのA
を表現できる- ただし、1 バイトの文字しか扱えないため
u8'あ'
などはエラー - なぜ
char8_t
が入らないのにこんなものを入れたのか
16 進数の浮動小数点リテラル
0x0.01p0 => 1/256 = 0.00390625
みたいな感じ。p
以下は 10 進数で指数部を指定する
属性名前空間を using できるように
[[using ns : foo, bar]] int x;
みたいにすると[[ns::foo, ns::bar]] int x;
と同じ動作になる
トライグラフの削除
- 詳しくは こちら
ライブラリ
全体的に触れるだけにします。ファイルシステムに関しては説明を放棄しています。
あと並び順はあまり重要度と関係がないため、実は重要な事が下に小さく書かれてたりするかも。
std::string_view
今まである文字列の部分文字列を取得したいときには substr
を使って次のようにしていた。
std::string str = "kitty on your lap."; std::cout << str.substr(2, 3) << std::endl; // => tty
これは便利ではあるが、一方で substr
の返り値が std::string
であるために、その生成を生成するための文字列コピーが走るというパフォーマンス的な問題が生じていた。
要はただ文字列の一部を参照するためだけにそのコピーを作るのは無駄という話。
というわけで文字列の参照のみを持つ std::string_view
というのが入った。<string_view>
を include することで使用可能になる。
これを使うとさっきのコードも
#include <string_view> //... std::cout << std::string_view(str).substr(2, 3) << std::endl;
こうすれば無駄なコピーが走ることはない。めでたしめでたし。
ただし、string_view
が参照している文字列を書き換えることは出来ない。つまり以下のコードは通らない
auto sv = std::string_view(str); sv[0] = 'a'; // エラー!書き換え不可能
std::optional
無効かもしれない値を表現するクラス、std::optional
ができた。<optional>
を include すると使える。
以下のようにして使う
#include <optional> // ... std::optional<int> f(int x) { int* res = hoge::proc(x); // ポインタを返す外部の関数 if (res) return *res; else return std::nullopt; }
以下のような事もできる
std::optional<int> opt = f(); std::cout << opt.value_or(42) << std::endl; // nullopt なら 42 として扱う
ようやく入った。しかし C++ なので opt.map(f)
みたいなやつはない。
std::variant & std::visit
プログラムを書いているとたまに union
を使いたい場面が出て来る。でも union
は型の概念を破棄しているに等しいクソだった。
C++17 ではまともな union
にあたる std::variant
が追加された。これも <variant>
ヘッダを include すると使える。
std::variant<int, double, std::string> var; var = 42; var = 5.0; var = std::string("kitty on your lap"); var = 'a'; // これはエラー
値を扱うにはこうする
if (auto p = std::get_if<int>(var)) std:cout << *p << std::endl; else if (auto p = std::get_if<int>(var)) std:cout << *p << std::endl; else if (auto p = std::get_if<int>(var)) std:cout << *p << std::endl;
しかしこれは流石に面倒なので std::visit()
という関数が用意されている。
std::visit([](auto&& x) { std::cout << x << std::endl; }, var);
神的に便利になる。型で処理をスイッチしたい場合はそういうクロージャーを作ってそのインスタンスを渡してやれば OK。便利 (大事なことなので 2 回言いました)
std::any
上の 2 つは適当に機能を制限していたが、それをぶっちぎってなんでも入る型 std::any
が追加された。<any>
を include すると使える。
使い方は std::variant
のもっと自由な感じなので省略。ただし値を取り出すときには std::any_cast()
を使う必要がある。
初学者であるほど使いたがりそうだが、初学者であるほど使ってはいけない機能個人的 No.1。本当に必要な時に伝家の宝刀として。
ファイルシステムを扱う機能が追加
ファイルシステムを扱う機能群としてとうとう <filesystem>
ヘッダが入った。また、この機能群はほぼ全て std::filesystem
名前空間上に定義される。
これに関しては 江添さんのやつ が非常に詳しい。というか僕には手に負えないので重要ではあるが存在の紹介にとどめさせていただきたい。
std::apply
std::visit()
の tuple 版っぽい std::apply()
が追加された。これで tuple の各要素に対する一様な操作が以下のように書ける。
std::tuple<int, double, std::string> tup = f(); std::apply([](auto&& x) { std::cout << x << std::endl; }, tup);
便利。
std::make_from_tuple
tuple の各要素をある型 T
のコンストラクタに突っ込みたいときがあるが、今まではいちいち補助関数を書く必要があった。
面倒なので std::make_from_tuple()
が追加された。
struct Hoge { Hoge(int, double, std::string); // 多変数を取るコンストラクタ }; std::tuple<int, double, std::string> tup = f(); Hoge hoge = std::make_from_tuple<Hoge>(tup); // こんな感じ
連想配列系コンテナ関連
ノード単体を扱う機能がついた
std::map
やstd::unordered_map
、std::multimap
や対応する set 系コンテナに、その要素のノードを扱う型であるnode_type
が追加された。- これは一見
iterator
のように見えるが遷移操作ができず、単に要素自体を表す型になる。 - これに合わせて、引数に渡した要素をコンテナから破棄してその
node_type
を取得するextract()
メンバと、2 つのコンテナを連結させるmerge()
メンバが追加された
その他のメンバ追加
try_emplace()
とinsert_or_assign()
が追加されたtry_emplace()
はemplace()
と違って要素が新たに挿入されなくても引数に渡した変数がムーブされないinsert_or_assign()
はoperator []
とほぼ同じだが、返り値が (要素の参照, 要素が追加されたかどうか) の pair になっている
<functional>
関連の色々
検索アルゴリズムの追加
<functional>
ヘッダに検索アルゴリズムがいくつか追加された。- ボイヤー・ムーア法とかがある
std::not_fn
- 「bool に変換できる型を返す関数の返り値を反転させる関数」を返す
std::not_fn()
が追加された - 名前が楽しくなさそうという印象が強い
- 合わせて
std::not1()
やstd::not2()
、std::unary_negate()
などは非推奨化された
std::invoke
- 「関数を呼び出す関数」である
std::invoke()
が追加された - 嬉しい人には嬉しい
<type_traits>
関連の色々
std::void_t
- テンプレート引数に何を渡しても
void
になる型、std::void_t
が入った - 一見意味がなさそうに見えるが、SFINAE で便利になる。
変数テンプレートの追加
std::is_hoge<T>::value
とかstd::is_hoge<T>{}
の代わりにstd::is_hoge_v<T>
と書けるようになった
std::invoke_result への移行
std::invoke_result<F, Args...>
で、型F
の関数を型がArgs...
の引数で呼び出したときの返り値の型が取得できる- 元々
std::result_of
があったが、ちょこちょこ問題があったためこれは非推奨化され、代わりにstd::invoke_result
が出来た
std::bool_constant
- コンパイル時の型として区別できる bool 定数を表現するための
std::bool_constant
が入った - 今までなかったのが驚き
論理演算系の追加
- 否定、論理積、論理和 などが入った
- 例えば論理積なら
std::conjunction<std::is_foo<T>, std::is_bar<T>>::value
みたいな感じで書ける
その他にもなんか色々ある
メモリ系の色々
std::shared_ptr が配列に対応
- タイトル通り。なぜいままでなかったのか
shared_ptr::weak_type
std::shared_ptr<T>
のメンバ型にstd::weak_ptr<T>
に対するエイリアス、shared_ptr::weak_type
が入った
memory_resource
- メモリの確保/解放に関する新しい管理機能群が入ったヘッダ
<memory_resource>
が出来た - 要は次世代版アロケータ
メモリ管理機能機能の追加
<memory>
に色々入った
キャッシュライン定数が追加
<new>
ヘッダにキャッシュ効率を色々頑張りたい時用のコンパイル時定数、std::hardware_destructive_interference_size
とstd::hardware_constructive_interference_size
が入った
その他
重要なものも混ざっています
並列アルゴリズム
- みんな大好き(?)
<algoriothm>
ヘッダの関数の並列化バージョンが入った std::sort(std::execution::par, std::begin(vec), std::end(vec))
みたいにすると使える
コンテナに関するフリー関数の追加
<iterator>
ヘッダにstd::size()
やstd::empty()
、std::data()
が追加された- それぞれ(その機能が提供されていれば)配列を含めるコンテナの要素数、コンテナが空かどうか、要素の配列の先頭のポインタを取得する
std::scoped_lock
- 今まで複数の変数を lock_guard するのは名前付けとかが面倒だった
- C++17 では
std::scoped_lock locks(x, y, z);
みたいにすることでx, y, z
全てに対する lock_guard を取得できるようになった
std::shared_mutex
- 書き込みが少なくて、読み込みが多い変数の mutex を効率的に行う
shared_mutex
が追加された - flag とかに使えそう(?)
std::as_const
const
でない変数にconst
を一時的に付与したい時が割りとあるが、const auto& cx = x
みたいにするかconst_cast
をするしかなかった- 不便なので
<utility>
にstd::as_const()
が入った
std::clamp
<algorithm>
ヘッダにある値を一定範囲に丸める関数、std::clamp()
が入ったstd::clamp(val, min, max)
で値が[min, max]
の範囲に入るよう丸められる- 数だけでなく一般に使えるので
<cmath>
じゃないっぽい
<cmath>
ヘッダに色々入った
- ベータ関数だのルジャンドル多項式だののすごい数学関数がいっぱい入った
- 三次元版の
std::hypot()
とかも入った
std::gcd() & std::lcm()
std::gcd()
とstd::lcm()
が入った<cmath>
ヘッダではなく<numeric>
ヘッダなので注意- 僕は性根が腐っているので早くこれに気付かずに死んでいく
using namespace std
er の顔が見たい
emplace 系関数の返り値が付いた
- 今まで
vector::emplace_back
などの emplace 系関数の返り値はvoid
だった - C++17 からはこれが挿入された要素の参照を返すように
- つまり
auto& back = vec.emplace_back(5);
みたいな事が出来る
std::uncaught_exceptions
- まだキャッチされていない例外の個数を取得する
std::uncaught_exceptions
が入った - 同時に
std::uncaught_exception
は非推奨になった - 名前の区別がつきにくくて困る
その他の非推奨になったもの
std::iterator
shared_ptr::unique()
<codecvt>
ヘッダstd::allocator
の幾つかのメンバ
以上
どうでしょうか。色々抜けはあるかもしれませんが大体こんな感じだと思います。
個人的にはまあダメなところを上げれば concept がないだとか range がないだとか structured bindings が微妙だとか色々いえますけど、良く言えば「次に繋がるアップデート」になるかなと思います。
ただ地味に重大な問題として未だに C++17 を完全に実装したコンパイラが存在しない気がします。
というわけでみなさんも良き C++ ライフを。明日は I さんの「ネタはあるんですが,時間的余裕がない可能性が高く,万が一書けない場合は @azaika_ が代わりに」です!!!
DDCC2017 参加記
SuperCon で爆死して参加記を書いていなかったのでこっちを書きます。
DDCC is 何
ディスコ株式会社とディスカバリーチャンネルが共同で開催しているオンサイトのプロコン。Disco Discovery-channel Code Contest の略らしい(当日まで Disco Discovery Channel programming Contest だと思っていた)。
800人ちょい出た予選のうち上位の就活枠 100 人と一般枠 100 人が参加できる。オンサイトにしては緩めの予選。
ちなみに PCK の本選と同日に開催されていたが、PCK 予選落ちをキメていた僕には関係がなかった。
予選
AtCoder の Web サイト上で予選が行われ、それに参加した。難易度だけでいえば ABC くらいで、さらに D 問題が発想&場合分けみたいな感じだったので水色に優しかった。とはいえ僕は実装が遅れて 141 位くらいになり割りと不安だった。
100 位を下回っている場合就活枠が優秀な方が一般枠が通過しやすいので就活枠が優秀であることを祈っていた。結果、祈るまでもなく就活枠は優秀だったので通過することが出来た。
本選競技まで
大会側から「電源タップ少ないから必要なら早めに来て確保しろ」(意訳)みたいなメールが来たので早めに行かざるをえない感じになり、早寝早起きを目指したが早寝に失敗したので朝が辛かった。全部オデッセイとかいうやつが悪い。
そんなこんなで、眠い目を擦りながらなんとか受付開始前に会場に到着して完全に勝利したとか思っていたが、なんと前には既に結構行列が出来ていて悲しかった。
遅刻が酷いことで有名な技術オタクも電源騒動が入るだけで早起きするようになる事が分かったので、今後の参考にしたい
— Az (@azaika_) 2017年11月2日
受付で T シャツを受け取り、会場へ向かう。糖分補給用のチョコレートを配っていたのでありがたく頂戴してからタップのある空席を確保して安心していた。
その途中で eiya さんと合流するもその他の知り合いが一切見つからなかったため一人座席で JOI の過去問を解いていた。その後で、そもそも知り合いは PCK 本選に行っていて DDCC には来ていないことに気づいて辛くなった。気づいてから競技開始までは某デッセイのせいで眠かったので半寝していた。
競技中~終わり
ちょくだいさんのトークの後に競技が始まった。配点が「300-500-800-1100-1300」なので素直に前から解こうとした。
初っ端の A 問題で詰まりかける。10 分くらい経ったところで愚直にやるだけすればいいことに気づいて AC。非常に教訓的だった。
A 問題で時間を食ってしまったため焦って B 問題を開く。LCM が大好きなのでこっちは一瞬でわかり即実装したが、何故かバグる。8 分位してから扱う変数は int64_t
なのに作業変数だけ int
にしていたことに気づく。直したら通る。ちょっと反省した。
C 問題を見る。グラフだったので図を書いてみるも全然分からない。使えそうなアイディアは出て来るが考察に結びつかない。そんなことをやっているうちにコンテストが終わった。順位表凍結時点で 88 位くらいだったので、これから下がりそうな事を考えれば微妙な感じ。悲しくなっていた。
ところが、その後誰に聞いても C 問題が解けたという人がいない。凍結時点で AC していた Olphe さんに聞いてみても「乱択は最高」(文意を捻じ曲げる意訳)みたいな事を言われる。その後おなじく凍結時点で AC していた Square さんに聞きに行こうとするも、自明解の説明までされたところで大会側からのインタビューが入り中断。その後 Square さんと会話できる機会はなく、結局 C 問題の解法は謎に包まれていた。
その後は振る舞われた昼飯を食べつつ出会い厨に勤しみ、何人かに出会えた。昼飯はパスタに茹でられていない部位が多々あった事を除けば結構豪華で美味しかった。
午後にはプロ棋士の方と Ponanza の作者様と chokudai さんの対談を楽しく見たり、皆が社内見学ツアーに行く中で申し込み忘れた僕は Rust で競プロしてみたりしていた。その途中で問題の解説もアップロードされ、C 問題の解説を見て「理解できない…」とか言っていた。
あとは懇親会とかあってから結果発表。僕は 90 位で結局微妙だった。結局 C 問題を解けたのが 30 人程度しかいなかったため順位変動もあまりなく早解きコンっぽかったので、それならもうちょっと上を目指したかった。
最後に DDCC 特製ケーキ(豪華&美味しい)が振る舞われたり、 chokudai さんと写真を取らせていただいたりして解散した。写真は家宝にしたいです。