プログラミング/C++/C++テクニック の変更点
更新- 追加された行はこの色です。
- 削除された行はこの色です。
- プログラミング/C++/C++テクニック へ行く。
- プログラミング/C++/C++テクニック の差分を削除
[[公開メモ]]
#contents
* 概要 [#va06a887]
C++ は長らく触っていなかったので、
えっ、と思うようなことを知らずに苦労しています。
覚えた内容をここにメモ。
* テンプレート引数クラスへの friend 指定 [#b7e0f054]
cygwin 上の g++ (GCC) 4.3.4 で、
LANG:cpp(linenumber)
template<class T>
class Test
{
friend class T;
};
としたところ friend の行で
LANG:console
$ g++ test.cpp
test.cpp:4: error: using template type parameter 'T' after 'class'
test.cpp:4: error: friend declaration does not name a class or function
というエラーが出てしまいました。
テンプレート引数となった型 T に対して、
正攻法では friend 関係を構築できないようなのです。
これは C++ の仕様らしいのですが、これを回避するための方法として、
以下を見つけました。
LANG:cpp(linenumber)
template<class T>
class Test
{
struct alias_maker { typedef T T_alias; };
friend class alias_maker::T_alias;
};
凄く小手先なんですが・・・いいんですかね、これで。
この件について http://www.byte.com/documents/s=9162/cujexp0312wilson2/
に詳しい解説があったようです。
今は見られなくなっていたので webarchive 経由で見たところ、http://replay.waybackmachine.org/20041212160407/http://www.byte.com/documents/s=9162/cujexp0312wilson2/
この回避方法および、その他の回避方法に関する互換性情報がまとめられていました。
||||BGCOLOR(YELLOW):||c
|Compiler |Form #1 |Form #2 |Form #3 |Form #4|
|Borland C++ (5.51 & 5.6) |Yes | | | |
|CodeWarrior (7 and 8) | |Yes |Yes |Yes|
|Comeau (4.3.0.1) |non-strict only |non-strict only |non-strict only |non-strict only|
|Digital Mars (8.26-8.37) |Yes |Yes |Yes |Yes|
|GCC 2.95 |Yes | | | |
|GCC 3.2 | |Yes |Yes | |
|Intel (6 and 7) |Yes |Yes |Yes |Yes|
|Visual C++ (4.2 - 7.1) |Yes | |Yes |Yes, except 4.2|
|Watcom (11 and 12) |Yes |Yes |Yes ||
だそうです。
上記の解決法はこのうち Form #3 にあたり、~
何にも考えずに friend T と書くのが Form #1、~
何にも考えずに friend class T と書くのが Form #2、~
friend_maker をテンプレートクラスにしてもう一段ややこしくしたのが Form #4~
です。
リンク先では #ifdef を使って Form #1 と Form #3
とを選んで使うコード例も挙げられていました。
* ローカルな static 変数の初期値 [#s970d922]
cygwin 上の g++ (GCC) 4.3.4 で、
LANG:cpp(linenumber)
int test()
{
static int i = 0;
return i;
}
#include <iostream>
int main(int argc, const char* argv[])
{
std::cout << test() << std::endl;
}
の結果が、
LANG:console
$ g++ test.cpp
$ ./a.exe
67600
となって困っています。
納得できないことに、3 行目を i = 0 ではなく i = 1 にして、
LANG:cpp(linenumber)
int test()
{
static int i = 1;
return i;
}
では正しく 1 が表示されます。
LANG:console
$ g++ test.cpp
$ ./a.exe
1
初期値が 0 の時のみ、ローカル static 変数の初期化がうまく行っていないのです。
ちなみに、i に初期値を与えず static int i; としても、
結果は 0 を与えたときと同じでした。
そういう仕様でしたっけ???
もしかしたら cygwin のローダーがおかしい???
* static コンストラクタのようなもの [#sd28d8f7]
C# には static コンストラクタというのがあって、
static メンバーの初期化等に便利に使えます。
C++ には static コンストラクタはないので、
代替法を探したところ、
http://cppdiary.blog76.fc2.com/blog-entry-11.html
を見つけました。
グローバル変数の初期化タイミングで static
関数を呼び出そうという企みのようです。
似たようなことを次のようにしてやってみました。
initial_callback.h
LANG:cpp(linenumber)
#ifndef INITIAL_CALLBACK_H
#define INITIAL_CALLBACK_H
template<class T, void func()>
class InitialCallback
{
public:
InitialCallback() { func(); }
};
#define INITIAL_CALLBACK(t, f) \
InitialCallback<t, t::f> t##_InitialCallback_##f;
#endif
を作って、次のように使います。
initial_callback_test.cpp
LANG:cpp(linenumber)
#include "initial_callback.h"
#include <iostream>
class Square
{
// InitialCallback を使って table を初期化する
static int table[256];
static void StaticInitialize()
{
for(int i = 0; i < sizeof(table); i++)
table[i] = i*i;
}
public:
static int square(int i)
{
return table[i];
}
};
int Square::table[256];
// この宣言で Square::StaticInitialize が初期化時に呼び出されます
INITIAL_CALLBACK(Square, StaticInitialize);
int main(int argc, const char* argv[])
{
std::cout << Square::square(1) << std::endl;
std::cout << Square::square(2) << std::endl;
std::cout << Square::square(3) << std::endl;
std::cout << Square::square(4) << std::endl;
}
ミソは 21 行目の INITIAL_CALLBACK という宣言です。
ここにクラス名と static 関数名を与えておくと、
グローバル変数の初期化タイミングで、与えた関数が呼び出されます。
実行してみると、ちゃんと Square::table は初期化されて、
LANG:console
$ g++ static_initializer_test.cpp
$ ./a.exe
1
4
9
16
となりました。
** スコープによってはうまく行かない [#j738e917]
あう、入れ子クラスにこれを使おうとするとうまく行きませんね・・・
LANGUAGE:C++
INITIAL_CALLBACK(SuperClass::Square, StaticInitialize);
// InitialCallback<SuperClass::Square, SuperClass::Square::StaticInitialize>
// SuperClass::Square_InitialCallback_StaticInitialize;
// に展開されて、
// SuperClass::Square_InitialCallback_StaticInitialize
// が見つからないといってエラーになります。
その場合にはマクロを使わず、次のように書けばいいですね。
LANGUAGE:C++
InitialCallback<SuperClass::Square, SuperClass::Square::StaticInitialize>
SomeUniqueName;
SomeUniqueName のところは他とかぶらなければどんな名前でも大丈夫です。
* コメント [#m531470d]
#article_kcaptcha
**BKUUktaacwaLBa [#d68dc5d5]
>[ckvyok] (2013-01-05 (土) 22:41:43)~
~
WJlBeD , [url=http://clhueczncocw.com/]clhueczncocw[/url], [link=http://fogcjogmaake.com/]fogcjogmaake[/link], http://aumwklrtqztr.com/~
//
#comment_kcaptcha
Counter: 24247 (from 2010/06/03),
today: 4,
yesterday: 4