Chandler@Berlin -44ページ目

Chandler@Berlin

ベルリン在住

再び計算機ネタである.しかも C++ なので一般向けではないだろう.

gcc の -Wall を使っていると,コンストラクタの initializer list の初期化の順番が違うという warning がでることがある.例えば,

% g++ -Wall initializer_list.cc
initializer_list.cc: In constructor 'InitializerListTest::InitializerListTest()':
initializer_list.cc:30: warning: 'InitializerListTest::d_j' will be initialized after
initializer_list.cc:29: warning: 'int InitializerListTest::d_i'
initializer_list.cc:7: warning: when initialized here

のようなものである.これは class の内部で宣言されている member variable の順番とコンストラクタの initializer list の書かれている順番が違うという warning である.以下のようなコードで起こる.

---
#include <iostream>

class InitializerListTest {
public:
/// constructor
InitializerListTest() :
d_j(123), // j = 123;
d_i(d_j - 1) // i = j - 1;
{
// empty
}
/// print out
void print()
{
std::cout << "i = " << d_i << ", j = " << d_j << std::endl;
}

private:
/// i, j
int d_i;
int d_j;
};

int main()
{
InitializerListTest ilt;
ilt.print();
return 0;
}
---

この warning は,C++ では initializer の list と各メンバの constructorの呼び出しの順番には関係がないことに起因している.Stroustrup の C++,3rd edition の 10.4.6 Class Objects as Members には The constructorsare called in the order in which they are declared in the class rather than the order in which they appear in the initializer list. To avoid confusion, it is best to specify the initializers in declaration order. とある.私は日本語の訳の本を持っていないので,どう訳されているか知らないが,「(メンバの)コンストラクタの呼び出しは,initializer list に書かれている順番ではなく,クラス内で宣言された順番で行なわれる.混乱しないように,initializer は宣言された順番に書くのが良いだろう.」となるだろう.これが warningの意味である.上記のサンプルコードでは,initializer list は

d_j(123),
d_i(d_j - 1)

となっているので,一見すると,

d_j = 123;
d_i = (d_j - 1);

のように見えるが,実際に実行されるのは,

d_i = (d_j - 1);
d_j = 123;

の順番であり,d_i の値は不定である.私の環境では,このプログラムを実行させると,

i = 32766, j = 123

という結果になった.これは確かに混乱のもとであるので,gcc は warning を出すのであろう.ただし,このようにメンバは初期化の順番に依存するコードを initializer list に書くこと自体,私は避けることにしている.必要な場合にはコンストラクタの block の内部で行えば良い.初期化の順番にdependency があるものを initializer list に書いている,というようなwarning にしてもらった方がより嬉しい気がする.

タイトルの「C++ のコンストラクタにおける initializer list の順番は重要なのか?」に対しては C++ では Yes と答えて良いと思う.ところで,Lisp であれば,これは let と let* の違いである.Lisp の let はそれぞれのinitialize を並列に行なってもかまわず,let* は sequential である.

C++ では混乱しやすい implicit な let* しかなく,並列実行可能な let がないというのは実行速度至上主義の C++ としてはちょっと不自然な気もする.C++ の定義では難しい上に遅くなる傾向があって良い所がみあたらない.(それともそれも C++ の特性なのか?)

これは非常に限られた人向けの記事です.もし,あなたが Visual Studio C++ (2008) を使っていて,MT と MD という option が何を示しているのか知りたい場合には,もしかしたら面白いかもしれません.

私が STL を使った console application を Visual C++ 2008 上で開発している時,次のようなリンカエラーでアプリケーションが作成できないことがありました.

error LNK2001: unresolved external symbol __imp___CrtDbgReportW

これは Windows の runtime library に関係があります.まず,W がついているのは Unicode 関係なので Unicode を off にしましたが,解決できません.

これは C/C++ の Code 生成に関係があります.Multi-threaded Debug DLL(/MDd) あるいは Multi-threaded debug (/MTd) というものです(一文字しか違わないので注意).アプリケーションを作っている際に,何かの拍子で私はライブラリのコンフリクトがあるという warning が出力されていることに気がつきました.link プログラムがコンフリクトを避けるには ignore library を指定せよというので,メッセージに従って MSVCRT というライブラリを無視するようにしたのですが,これが問題だったようです

このリンクエラーを解決するために,Code Generation を全て Multi-threadedDebug DLL (/MDd) (for debug)としました.そして無視するライブラリをなくしました.これでリンクすることができました.しかし,この warnning は混乱のもとです.

そこで,同僚の T に,いったい MD とか MT とは何か,どちらを使うべきなのかを尋ねました.彼の回答は以下のようなものです.

- MT: Mutil-threaded static library. これはアプリケーションを配布するには良い.全部 static にリンクされているので,環境に依存しないからである.

- MD: Mutil-threaded Dynamic link library. しかし,完結したアプリケーションではなく,ライブラリを配布する際には,dynamically link の方が良いことが多い.なぜならば,あるライブラリに静的にランタイムがリンクされている場合には,バーションのコンフリクトがあるかもしれない.今プログラムを作成している環境のランタイムと,ライブラリが静的にリンクした環境のランタイムが同じ保証はない.たとえば,ライブラリの内部で使っているnew/delete と手元のランタイムの new/delete のバージョンが違う場合にはライブラリが new して手元のコードが delete するかもしれない.これはバージョンによっては問題だ.

つまり,このオプションはどんなアプリケーション,ライブラリを作るかに依存して決めなくてはいけないものです.選ばなくてはいけないのはめんどうだなと思ったのですが,二つある理由はわかりました.でもこんなに似ていると間違えやすいです.

最近ちょっと面白い論文を読んだ.

Kanich et al. Spamalytics: an empirical analysis of spam marketing conversion.

Spam のメイルは毎日来るが,本当に spam は効果があるのかという疑問がある.この論文はこの「実験」をしている.筆者らは Storm botnet を用いて実際に三億五千万通の spam mail を送信し,どれだけ fileter されるか,どれだけのuser の visitor があるか,そして実際にどれだけ実際の購入があるのかを調べている.実験で法律的な問題などをクリアするのが大変だったようである.結果は,筆者らは条件は常に変化するのでこの数字を他に使ったり,それを解釈するのは危険であると断っているが,28 日間のあまりの実験で 350 million のspam mail が送信され,82 million が deliver され (おそらく多くは spam のフォルダに分類されるだろうが,どれだけが spam として分類されたかは不明),10,000 以上の web site visitor があり,そして,28 の user が実際に薬の購入を行なった.実際には金銭の送受信はなかったが,もしあったとすると,2800ドルほどの transaction があったということである.一日約 100 ドルのtransaction である.彼らの spam 生成はそんなに aggressive でなかったので,もし,これが scale すると一日に 7000 から 10000ドルの transaction が見こめるということである.ただし,Storm master 計算機の保守などいろいろな経費,また,騙すのでなければ薬を実際に売るコストなどもうけがいくらになるかは場合による.しかし,実際にこれだけのデータが得られたというのは面白いと思う.