std::make_shared にまつわるエトセトラ

C++11が世に出てもう5年経つので世の中のC++erさんにはもう常識なハナシかとは思いますがロートルおじさんは最近初めて知りました。結構ビックリ。

std::shared_ptr( new T(args…) );
std::make_shared( args… );

make_sharedはてっきり記述を楽するだけのものかと思ってたら、オブジェクトと管理用のメモリ領域を一体で確保してくれるとのこと。
一方上のようにフツーにnewしたものからshared_ptrを作成すると、オブジェクトのメモリと管理領域のメモリがそれぞれ別々に確保される。

C++プログラマーよ!std::make_sharedを安易に使うべからず! : 株式会社CFlatの明後日スタイルのブログ
make_shared で確保されたメモリ領域は,それを参照する weak_ptr が無くならない限り解放されない : 野良C++erの雑記帳

・管理領域を一体で確保するからメモリの割り当て回数が少なくて済む。その分速い
・make_sharedが管理領域をまとめて独自にメモリを確保するのでクラスのoperator new/deleteが呼ばれない
・make_sharedの中からオブジェクトを構築するのでpublicでないコンストラクタを呼び出す手段がない
・オブジェクトを指すshared_ptrが全てなくなりdesutructされた後にまだそのオブジェクトを指すweak_ptrが残っている場合、管理領域を残す必要があるためにそれと一緒に確保された(destructされた後の)オブジェクトのメモリ領域が残ってしまう。そのオブジェクトを指すweak_ptrも全部無くなれば解放される。

うーん、make_sharedを使うか使わないか、結構意識しないといけないですねコレ。。。

C99の指示付き初期化子をC++で使っちゃっても良いのだろうか

C99の指示付き初期化子、便利ですよね。
でもC++11/14ではこれ定義されてない。

定義されてないんだけど、ClangなんかではC++と混ぜてフツーに使えてしまう。例えばこんな。

構造体メンバの初期化記法(構造体定義に直に初期値を記述)はC++11。そんで a の指示付き初期化リストの記述はC99。これで a は typeが0、valueが10 で初期化される。この書き方だと以下の利点がある。

・どのメンバをどの値で初期化するのかが判りやすい (C99)
・初期化の値の順番を気にしないで済む (C99)
・初期化で記述しなかったメンバはデフォルト値が設定される (C++11)

便利なんです。便利なんですが、Clangではコンパイル通るけど厳密にはアウトな書き方。(たぶん)

これが便利なのは例えば、「引数が沢山あって、でもどれもデフォルト値がある関数」。
C++の関数のデフォルト値は途中の引数だけ省略ってのができないのだけれど、引数を構造体でまとめてしまえば上記の方法で任意の引数(メンバ)だけを省略できるようになる。

こうすると、
・引数の値に名前を付けられて判りやすい
・引数の順番を気にせずに済む
・任意の引数を省略してconstexprなデフォルト値で初期化できる
のだ。

でも前述の通りC++規格的によろしくない。


追記:
インテルコンパイラ16.0ではやはりというか怒られる/(^o^)\
インテルコンパイラはC99オプションとC++11オプションを同時に有効にできないんだよな〜

CLLocationのdistanceFromLocation:の精度が低い。

低い。4桁程度だろうか。
7000mくらい離れた2地点の距離を求めると、国土地理院が公開してる測地線長の計算式で求めたものとの差が1mくらいになる。

これはまあ、ほとんどの場合は4桁もあれば十分ではある。

ただ、3地点を結んでできる三角形の面積をヘロンの公式で求めるのにこの distanceFromLocation: を使ってて困ったことが起きた。面積がNaNになることがあるのである。

調べてみると、3地点がほぼ直線上にある場合にdistanceFromLocation:で得られる長さで計算すると

  長辺の長さが他の2辺の長さの和よりも長くなってしまう

場合が生じることが判った。。。 これはマズい。三角形にならないじゃん?
先の有効桁数以下の差ではあるのでこれが起きた場合は面積ゼロとして処理する。。とかやればいいのだが、うーむ。。。

測地線長を計算するよりは distanceFromLocation:の方が精度悪いとはいえ計算速いしなー、と悩んでたのだが、あれ? もしかして CLLocationがalloc/deallocされる時間を考慮したらあんま変わらないんじゃね? と思って計測したら案の定測地線長計算する時間より CLLocaitonの alloc/distanceFromLocation:/dealloc にかかる時間の方が1.2倍くらいかかってるではないか。

つまりこういうことだ。サヨウナラ distanceFromLocation: !!!

ちなみに測地線長の計算式で求めた2点間の長さで面積を計算した場合では面積がNaNになることはなかった。ぐらっちぇ