C++についての覺書

C++についての細々した覺書を纏めておくための文書。「C++ Tips その1」といふ由來の古い文書もある。

unique_ptr<>

auto_ptr<>は非推奬。代りにunique_ptr<>を使ふべし。

無論、必要な場合にはshared_ptr<>などを使ふ。

「例外仕樣」非推奬化

MSYS2を導入し(その手順はこちら)、ClangでC++17に追從しよう、とか思つてゐたら、早速ぶち當たつたのが、「error: ISO C++17 does not allow dynamic exception specifications」といふ宣告。どういふことかについては「非推奨だった古い例外仕様を削除」に詳しい。

左邊値、右邊値の話

ムーヴだのなんだのを扱ふ際には把握しておかなければ困るのだが、細かいところがなかなか把握できないものの一つ。

調べ物をしてゐるときに割と良い感じの説明を見附けたので、備忘としてリンクを張つておく。

std::remove(あるいはstd::remove_if)の羂

<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}となる。

亂數

ツイッターに書いたものの、再び探し出す羽目に陷つたので覺え書きしておく。

#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

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される。
}

なるほどさういふ擧動なのね、と諒解できたので、個人的には滿足。