C++についての覺書
C++についての細々した覺書を纏めておくための文書。「C++ Tips その1」といふ由來の古い文書もある。
unique_ptr<>
- 執筆: 平成27年5月21日
auto_ptr<>は非推奬。代りにunique_ptr<>を使ふべし。
無論、必要な場合にはshared_ptr<>などを使ふ。
「例外仕樣」非推奬化
- 執筆: 平成30年12月21日
MSYS2を導入し(その手順はこちら)、ClangでC++17に追從しよう、とか思つてゐたら、早速ぶち當たつたのが、「error: ISO C++17 does not allow dynamic exception specifications」といふ宣告。どういふことかについては「非推奨だった古い例外仕様を削除」に詳しい。
左邊値、右邊値の話
- 執筆: R1.9.10
ムーヴだのなんだのを扱ふ際には把握しておかなければ困るのだが、細かいところがなかなか把握できないものの一つ。
調べ物をしてゐるときに割と良い感じの説明を見附けたので、備忘としてリンクを張つておく。
std::remove(あるいはstd::remove_if)の羂
- 執筆: R3.4.12
- 修正: R4.8.28、R6.4.14
<algorithm>に入つてゐるstd::remove(first, last, v)
あるいはstd::remove_if(first, last, pred)
の擧動は、[first, last)のうちの殘すべき要素をfirstから順に間を開けず順序が保存されて竝ぶやうにmoveし、殘すべき最後の要素の次を指すイテレータ(newlast)を返す、といふもの。[newlast, last)に要素が存在したまま(殘すべき要素をmoveした、いはば"跡地"なので、値は「未規定」、要するに分からない)になつてゐることに注意せねばならない。
もう少し嚙み碎いて説明しよう。std::vector<int> vecA = {1, 5, 2, 4, 5, 3};
なるvecAがあつたとする。こいつから5を削除しようとしてstd::remove(vecA.begin(), vecA.end(), 5);
としたとき、vecAの中身は{1, 2, 4, 3}とはならない。{1, 2, 4, 3, X, Y}のやうになり、3の次(つまりXの位置)を指すイテレータを返す。vecA.size()
の返す値に變化は無い。
故に、要らないものを完全に消し去るには、newlast = std::remove(vecA.begin(), vecA.end(), 5);
としておいた上で、vecA.erase(newlast, vecA.end());
のやうにする必要がある。
- 參考資料
- remove - cpprefjp C++日本語リファレンス
R3.5.1追記
C++20では、STLのコンテナに對しては、std::erase(container, v)
やstd::erase_if(container, pred)
が追加されたので、樂になつた。まさかremoveしても實際には消えてへんとか思はへんもんな。vector, list, deque, forward_listに對しては兩者ともに、set/map系に對しては後者のみ、追加された。
上のvecAの例であれば、std::erase(vecA, 5);
とやれば、vecAの中身が{1, 2, 4, 3}となる。
亂數
- 執筆: R5.3.2
ツイッターに書いたものの、再び探し出す羽目に陷つたので覺え書きしておく。
#include <random>
// 亂數生成器の定義
std::random_device seedGen; // 非決定論的亂數生成器。シード生成に利用。
std::mt19937_64 engine(seedGen()); // 64bit版メルセンヌ・ツイスター法による疑似亂數生成器
std::uniform_int_distribution<> dist(1, 6); // 1〜6の整數を一樣分布で得るためのdistribution
// 亂數の生成
int x = dist(engine);
疑似亂數生成器を用ゐずstd::random_device::operator()
をガンガン呼び出すやうなことも出來るみたいだが、コストとかどうなのかねえ。
std::any
- 執筆: R6.4.1
std::any
は、コピー可能なものなら何でもぶち込める便利な型である。中身を取り出すときは、std::any_cast<T>()
を用ゐるが、指定するTはぶち込んだものと同じでなければならない、單に變換可能なだけでは駄目、と文書には書いてある。なるほど。
そこでふと思つたのは、とあるクラスAを繼承するクラスBのオブジェクトを指すAへのポインタをぶち込んだらどうなるのか、みたいなこと。むろん多態性(polymorphism)を使つてゐること前提。ああなのかそれともかうなのか、と考へてゐても仕方ないので、以下のやうなコードで試驗してみた。使つたコンパイラはGCC13.2.0。出力結果はコメントに示す。
#include <iostream>
#include <any>
#include <typeinfo>
struct A
{
virtual ~A() = default;
virtual void operator()() const { std::cout << "A" << std::endl; }
};
struct B : public A
{
virtual void operator()() const override { std::cout << "B" << std::endl; }
};
int main()
{
A a;
B b;
A& ra = b;
a(); // 出力: 'A'
b(); // 出力: 'B'
ra(); // 出力: 'B'
std::any x = &a;
std::any y = &b;
std::any z = (A*)&b;
std::any v = &ra;
std::cout << x.type().name() << std::endl; // 出力: A*を表す文字列
std::cout << y.type().name() << std::endl; // 出力: B*を表す文字列
std::cout << z.type().name() << std::endl; // 出力: A*を表す文字列
std::cout << v.type().name() << std::endl; // 出力: A*を表す文字列
(*std::any_cast<A*>(z))(); // 出力: 'B'
// なほ、std::any_cast<B*>(z) を呼ぶと std::bad_any_cast が throwされる。
}
なるほどさういふ擧動なのね、と諒解できたので、個人的には滿足。