藻ログ

ごぼごぼごぼ

C++ゆとり用

C++

藻はC++を自由自在に使いこなせる*1人を尊敬します.

世の中には藻のようなライトC++書き向けの資料が不足しているので,
大学院での普段使い(どんな普段だよ)用途で収集したものをまとめてみました.

間違ったことを色々書いてそうなので, 見つけたら直します.

はじめに

エディタの設定で*.cpp *.hのタブ幅を2(スペース2こ)にして縮小を図ります.
長ったらしい記述が増えるので, タブ幅2が読みやすいです(自分は)

clangを使う

Clang - Wikipedia
gccよりもclangの方がC++ 規格に準拠していると言われています.
warningが増えるかもしれませんが, デバッグメッセージが親切なのでg++のメッセージがイミフなときに
clang++でコンパイルすると意味がわかって解決することがあります.

標準ライブラリを使う

 ∧∧∧∧∧∧∧∧∧
STL!STL!STL!STL! >
 ∨∨∨∨∨∨∨∨∨
 _     _
`/っ)    /っ)
/ / ∧_∧ / / ∧_∧
\\(   )\\(   )

便利なSTLコンテナが用意されているので活用します.
hashが見当たりませんが, unorderd_mapが一応それに相当するようです.
http://www.cplusplus.com/reference/stl/

sort, find, for_each, findなどやりたいことは大体algorithmにある気がします.

#include <algorithm>

http://www.cplusplus.com/reference/algorithm/

たまにどっちにあるのか混乱しますが,
accumulate, partial_sumとかはnumericにあります

#include <numeric>

http://www.cplusplus.com/reference/numeric/

iostream,fstreamやstringstreamなど,ほげほげstream群も便利

int toInt(string s) { int r = 0; istringstream ss(s); ss >> r; return r; }
string toStr(int n) { ostringstream ss; ss << n; return ss.str(); }

参照は便利

余計な一時オブジェクトを作るとメモリが無駄なので, なるべく参照渡しします.

void hogehoge(const std::vector<int>& vec) {
  std::cout << vec.size() << std::endl; // 実体のように扱えるが, 参照
}

Cだと class* obj とかポインタ受け取ってobj->propのようにアクセスしたり,
実体(コピー)を受けてobj.propのようにアクセスしたりでしたが, &参照が出来るようになって扱いやすくなりました. データを読み出し専用にしたい場合は, const宣言を忘れずに.

constを使う

わたしはかつてconstを全く活用していなく, 「漢のC++」というお言葉を頂戴したことがあります. *2

constが全くついていない. こいつら(メソッド)は全員容疑者ってことだ.
きみはコンパイラの力を全く駆使していない

class GeodesicDome {
  private:
    const double radius;
    const int split_depth;
  public:
    std::vector<double> vertices;

  public:
    GeodesicDome(double radius, int split_depth) :
                  radius(radius),
                  split_depth(split_depth) {};
    void createFromIcosaHedron();
    void render() const;
    void dumpStatus() const;
    void output(const std::string& fname) const;
};

変更したくないオブジェクトにはconstを付加し, インスタンス変数を変更しないメソッド宣言末尾にもconstを付加します. こうすると違反をした時にコンパイラが怒ってくれます.

constなポインタは記述が特殊なので注意が必要です.

残念だったなそのconstは外せる

constつければ安心・安全!!! はい, const_castでconstを取ることができます.
使うとイケナイことに手を染めている気分になれます.

char* s = "hogehoge";
int size = strlen(const_cast<const char *> (s); // strlenはsを変更しないので, 信用してcast

inline展開

inline宣言すると呼び出し元に展開されるので, 関数呼び出し時のオーバーヘッドを抑えることができます. 短くて何度も呼び出される関数はinline宣言にすると良い.

inline double hoge() {
  //なんかシンプルな処理
  return xxx;
}

コンパイラの最適化オプションについては
http://www.cqpub.co.jp/interface/column/freesoft/2002/200208/08-4.htm
展開すればするほど実行ファイルのサイズは大きくなり, コンパイルも遅くなるので注意します.

C++11の機能を使う

お使いのコンパイラは最新ですか?
C++11 - Wikipedia

$ g++ -std=c++11
$ clang++ -std=c++11

コンパイルできます. 全部は全く把握してないので使いやすいものを列挙

auto

型推論ができるようになりました.

auto x = 42; // int
auto y = 3.14; //float

std::vector<int>iterator it = v.begin() // C++03
auto it = v.begin() // C++11

もう長ったらしいイテレータを記述しなくていいんですね先生!!!!!

やさしいfor

for (const auto& v: vec) {
  std::cout << v << std::endl;
}

for (const int x: { 1,2,3,4,5,6 }) std::cout << x << std::endl;

なぜいままでできなかったのか
参照&無しで受けると要素のコピーに(なったはず)

initializer list

要素の初期化がとても楽になりました.

std::vector<int> v0 = { 1,0,0 };
std::vector<int> v1 = { 1,0,0 };
std::vector<int> v2 = { 1,0,0 };
std::vector<std::vector<int>> v3 = { v1,v2,v3 };
std::vector<std::vector<int>> v4 = { {1,2,3}, {1,2}, {1} };

コンストラクタにも.

return std::vector<int>({1,2,3});

array

固定長配列が仲間入りしました.
使ってないけど,beginやendが使えるようになって, 生ポインタを触らなくてよくなった?

int* array = new int[5]; // 今までの
std::array<int,5> = {1,2,3,4,5}; // 固定長配列

nullptr

C C++
NULL (void*)0 0

CではNULLはvoid*でしたが, C++ではマクロで0になってるので謎挙動の原因になります.
関数オーバーロードで引数が異なるものがあるとき

void hogehoge(char* foo, int bar);
void hogehoge(char* foo, void* bar);

第2引数でNULLをえいやと渡すと, intと解釈されてvoid hogehoge(char* foo, int bar);
の方が呼び出されてしまうので,

hogehoge(_foo, (void*)NULL);

とかvoid*キャストして頑張ってましたが, nullptrを渡せばよくなりました.
NULLはマクロで定義された0ですが, nullptrはぬるぽであることをコンパイラに教えることができます.

static_assert

cassertのコンパイル時評価版, static_assert
第2引数で文字列をとれるので, &&しなくてよくなった

assert(a==b && "[ERROR] a is not equal to b"); // 実行時
static_assert(a==b, "[ERROR] a is not equal to b"); //コンパイル時

暗黙的コピーコンストラクタの禁止

private宣言するとか頑張らなくても, deleteを指定すればコピーコンストラクタの使用を禁止できるようになりました.

class NoCopy {
NoCopy(const NoCopy&) = delete;
NoCopy& operator=(const NoCopy&) = delete;
}

ラムダ

ラムダ関数が定義できるようになりました.

[](int x, int y) -> int { int z = x + y; return z + x; }
[](int x, int y) { int z = x + y; return z + x; } // return val; の場合は戻り値型を省略できる

wikiによると[]内は

[]        //ラムダ関数外のどの変数も使うことができない。
[x, &y]   //xはコピーされる。yは参照渡しされる。
[&]       //すべての外部変数は、もし使われれば暗黙的に参照渡しされる。
[=]       //すべての外部変数は、もし使われれば暗黙的にコピーされる。
[&, x]    //xは明示的にコピーされる。その他の変数は参照渡しされる。
[=, &z]   //zは明示的に参照渡しされる。その他の変数はコピーされる。

のように扱えるようだ.

今まで外部関数を宣言していたのが, スマートに書けるように.
これでPerlとかでいうmapとかgrepも書きやすくなるんですかね.

#!/usr/bin/perl
my $prices = [ 98, 4980, 12500 ];
my $pay = [ map { $_ * 1.08 } @$prices ]; #=> [105.84,5378.4,13500]
my $found = [ grep { $_ > 1000 } @$prices ]; #=> [4980,12500]

mapはこんな感じか

// map func
const double scale = 1.08;
std::transform(v.begin(), v.end(), result.begin(),
               [scale](int x) -> double {return x*scale }); 

全然使いこなしてないので, 色んなユースケースを読んでみたい
本の虫: lambda 完全解説

boostを使う

Boost C++ Libraries

Boost のプログラムソースは初心者にはとても読めるものではなく、またテンプレートを駆使するためコンパイル速度が非常に遅いです。
特に有用な機能は C++11 に取り込まれていますので、Boost を使うことはお勧めしません。

モダン C++ プログラミング - 日本語公開記事 - サイボウズエンジニアのWIKI
boostにあったような機能がC++11に組み込まれて標準化され...というのが結構あるようです.
C++11とBoostの対応付け - boostjp

Boost のプログラムソースは初心者にはとても読めるものではなく、

はい, とても読み辛いです.

boost_program_optins, boost_regex ぐらいしか使ったことないです(C++11のstd::regexの挙動が謎かったので)

splitはいつも自作で悲しいのだが, STLで何故かみあたらない...(実はあるのか・・?)
boostにはstring_algorithmが一応ある
http://www.boost.org/doc/libs/1_56_0/doc/html/string_algo.html

boostないと何もできなくなった

用法・容量に注意しましょう

ふええ...コンパイルが遅いよぉ...

なんでもboostのせいにするのはよくない

その実装、もうあります

車輪の再発明を避けます. CやC++だとなんでも自作しがちですが, 目的の実装は先人が既に実現しているはずなのです.
標準ライブラリとboostから検索だ!!

C++はなるべく書かない

最後に何いってんだよという感じですが, 研究ではC++はなるべく書かないようにしています.
勿論どうしてもC++でないと実装できない箇所はC++で作ります.
ただ, LLで書いた方が遥かにコストが低いパーツが多いので, C++で作る部分はコマンドラインオプションを受け取ってデータシートを吐く機能にとどめて, LLでラップして回したりしています.

リファレンス(vim用)

おお, こんな便利なものが...知らなかった

rhysd/unite-n3337 · GitHub


osyo-manga/unite-boost-online-doc · GitHub

あっ

スマートポインタのこととか全然書いてない.....................................

*1:黒魔術を使えるという意味ではない

*2:もちろん叱咤である