はじめまして。へっちんといいます。
VisualC++7で以下のコードをビルドすると不可思議なエラーが発生
するのですが、エラー発生の原因、対処方法などご存知の方がおいで
でしたら教えて頂きたいです。
# VisualC++6では普通にビルドできるのですが・・・
環境:WinXP Pro SP1
#include <iostream>
struct HogeBase {
virtual void Do() = 0;
};
template <class T>
class Hoge : public HogeBase {
public:
Hoge(T t) : m_t(t) {}
void Do() { m_t(); }
private:
T m_t;
};
class HogeUser {
public:
template <class T>
HogeUser(T t) : m_p(new Hoge<T>(t)) {}
~HogeUser() { delete m_p; }
void Do() { m_p->Do(); }
private:
HogeBase *m_p;
};
struct HogeTool {
void operator ()()
{
std::cout << Tool for Hoge... << std::endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
// これはOKなんですが・・・
// HogeUser hoge_user = HogeTool();
// この作成方法だとエラーとなります
HogeUser hoge_user(HogeTool());
// この行がエラー発生行で以下のエラーメッセージが出力されます
// 『error C2228: '.Do' : 左側がクラス、構造体、共用体ではありません。』
hoge_user.Do();
// ちなみに動的生成なら問題なし
// HogeUser *hoge_user = new HogeUser(HogeTool());
// hoge_user->Do();
return 0;
}
なぜだかはまだ説明できませんが、これならOKでした。
HogeTool t;
HogeUser hoge_user( t );
hoge_user.Do();
monkey さん、レスありがとうございます。
こちらでも確認しました。
先に作ったインスタンスを指定するのはOKのようですね。
VC++7の仕様?それともバグ?
マウスカーソルを
>> hoge_user.Do();
の部分のインスタンス名のところに持っていくとIntelliSenseによって
「HogeUser hoge_user(HogeTool (*)(void))」
と表示されます。
普通 HogeUser hoge_user; と宣言してるわけですから
「HogeUser hoge_user」
と表示されそうなものですが。
もはや型として別物になってしまっているんですかね。
この問題はEffective STLに載っています。
> HogeUser hoge_user(HogeTool());
これは,「引数を取らずHogeToolを返す関数へのポインタ」を引数に取り,
HogeUserを返す関数の宣言として扱われます。
まず,関数宣言はオブジェクトの定義よりも優先されます。
see) ISO/IEC 14882:2003 8.2 Ambiguity resolution / Paragraph 1
次に,関数の宣言
HogeUser func (HogeTool a());
に関して,関数宣言中の仮引数の識別子は省けるので,これは
HogeUser func (HogeTool ());
と書くことが出来ます。
see) ISO/IEC 14882:2003 8.3.5 Functions / Paragraph 1他
さらに,関数の仮引数の型が関数型の場合,
それはその関数型へのポインタ型に暗黙のうちに変更されます。
see) ISO/IEC 14882:2003 8.3.5 Functions / Paragraph 6
よって,
HogeUser func (HogeTool ());
は
HogeUser func (HogeTool (*a)());
とみなされることになります。
とりあえず,
HogeUser hoge_user((HogeTool()));
としてやれば,HogeUser型の変数hoge_userの定義として扱われるはずです。
#VC++ 6.0では通らないかも。
> HogeUser hoge_user(HogeTool());
これを
引数を持たないHogeToolを返す関数をうけ、HogeUser を値で返す関数hoge_user
のプロトタイプ宣言、と解釈してますね。面白い。
ありゃ、かぶった、
EffectiveSTL 第6項ですね、
なるほどです。
これ↓が通った時点で気づいてもよさそうなものでした。
> HogeTool t;
> HogeUser hoge_user( t );
# もう一度EfectiveSTL読みなおそう!
な~るほど。
言われてみると確かに関数宣言に見えます(- -;
Effective STLを読んでおくべきでした。勉強不足を痛感・・・
ISOの規格であるなら、むしろVC++6.0で
> HogeUser hoge_user(HogeTool());
が問題なく通ることの方がおかしいということですね。
monkeyさん、YuOさん、PAIさん、どうもありがとうございました。
解決の後に書けるのだろうか?
ちなみに上記の問題はg++(3.2.2)では通りません。
(通らない方がC++として正しい)
よく、VC++6.0はtemplate周りの実装が弱いと言われますが、、
この事例は初めて見ました。
(古いコンパイラなんだから仕方が無いと言えばその通りですけどね)
> ISOの規格であるなら、むしろVC++6.0で
>> HogeUser hoge_user(HogeTool());
> が問題なく通ることの方がおかしいということですね。
いや、この行自体は通っていいのよ。
これによって インスタンス hoge_user ができてしまったことがマチガイなわけで。
解決の後ですが。
> Bosscat さん
> よく、VC++6.0はtemplate周りの実装が弱いと言われますが、、
はい。
VC++7でできないってことは、VC++6のはできてるように
見えてるだけかと思いました。
とすると怪しいのはテンプレート周りかなーとか。
# 何の根拠もなく・・・
> επιστημη さん
> いや、この行自体は通っていいのよ。
> これによって インスタンス hoge_user ができてしまったことがマチガイなわけで。
そうでした。失礼。
あの行はもはや関数のプロトタイプなのでした(- -;
解決の後に2回も書くなんて・・・w
すごいタイムリーな話があったんで独り言・・・
現在仕事(g++)でソケットサーバー機能を持ったtempleteを書いたのですが
それは、template引数にコールバック関数型を取っており
色々なクラス型にデコードした状態でコールバックされるのです。
これをVC++側でも使う話が浮上していて
私は「まあ問題ないんじゃない?」と答えたのですが、、
実は、こんな問題があると、コンパイルが通らない可能性がありますねぇ~
これから検証しなければ・・・
終わってから3回目w
VC++6.0でtemplate仮引数に関数型を指定した場合の動作が気になって調べてみましたが、、
コンストラクタ以外の関数では問題無いようです。
そもそも、よく考えてみれば、YuOさんのおっしゃる通り
「関数宣言」と「オブジェクト定義」で名前がぶつかった場合の問題なので
クラス名と同時に関数名でもある、コンストラクタに特化した問題なことは明白なんですけど
ね、、
(namespaceを切って、無理やりクラス名と関数名をぶつけて見ましたが、ちゃんと「名前空間
があいまいです」と言ってきましたよ)
まあ、ぶっちゃげた話、VC++6.0が信じられなかったんで色々やって見ちゃいましたw