dポイントプレゼントキャンペーン実施中!

コンパイラはVC++2008です。
いろいろあって、あるクラスにおいて関数ポインタと関数オブジェクト双方を
同じように利用できないかと考えて、次のように試みました。

class Base
{
public:
    virtual void func() =0;
};

template<class Func>
class CFunc :public Base
{
private:
    Func m_func;
public:
    CThreadFunc(Func func):m_func(func){}
    void func(){m_func();}
};

class Hoge
{
private:
    Base* base;
public:
    template<class Func>
    Hoge(Func func)
      :base(new CFunc<Func>(func))
    {}
    ~Hoge()
    {
      delete base;
    }

    void DoSomething()
    {
      base->func();
    }
};

クラスをテンプレートにするといちいち指定しなければならないので、
まず基底クラスに適当な仮想関数を設け、それを継承したクラスをテンプレートにしました。
そしてコンストラクタの引数で何かしらを受け取って、オーバーライドした関数の中で
関数ポインタか関数オブジェクトだと仮定して呼び出しています。
さらに基底クラスのポインタを目的のクラスが保持してやり、
こちらはコンストラクタをテンプレートにすることで引数から型を推測してもらうことで
先ほどのテンプレートクラスのインスタンスを作成しています。

そしてポインタを介してfunc()を使ったり…、などすれば、
とりあえず引数なしの関数と関数オブジェクトを同等に扱えないかなと思ったからです。


で、このようなクラスを作成してコンパイルすると、

void func(); //何かしら処理する関数

class Function
{
public:
  void operator ()();  //何かしら処理する関数オブジェクト
};

があったとして、
int main()
{
  Function function;
  Hoge hoge(function); //いったん作ってから渡す
  Hoge hoge2(func); //関数を渡す
 
  hoge.DoSomething();
  hoge2.DoSomething();
  
}

は動きました。しかし、
int main()
{
  Hoge hoge(Function()); //引数を初期化する
}

とすると次のようなエラーが出ます。
warning C4930: 'Hoge hoge(Function(__cdecl *)(void))':
プロトタイプされている関数が呼び出されませんでした (変数の定義が意図されていますか?)

また、
int main()
{
  Hoge hoge(Function()); //引数を初期化する
  hoge.DoSomething();  //クラスにアクセス
}
とすると次のようなほかのエラーが出ます。

error C2228: '.DoSomething' の左側はクラス、構造体、共用体でなければなりません。

しかし、例えば関数オブジェクトのコンストラクタに引数が設定されていたとして、
class Function
{
public:
  Function(int dummy);  //何か値を受け取る
  void operator ()();  //何かしら処理する関数オブジェクト
};
となっていた時、
int main()
{
  Hoge hoge(Function(1)); //引数を初期化する
  hoge.DoSomething();  //クラスにアクセス
}
の呼び出しは正常にコンパイルされ、想定通りの動きをします。
全く使わなくても、一つ以上の適当な引数を何でもいいからコンストラクタが持てば、
普通にコンパイルされるみたいです。ただ、デフォルト引数を与えてHoge hoge(Function())と
同じ形ですと引数があってもできないみたいです。

まったく通らないなら最初からあきらめるですが、中途半端にちゃんと動くために
エラーの原因を知りたいと思っています。
テンプレートの場合には、引数に渡すタイミングで初期化はしてはいけないのでしょうか?

A 回答 (2件)

蛇足っぽいけどちょっと補足:


「衝突」ってのは日本語として変でしたね. C++ の規格では
定義とも宣言とも取れるものはすべて宣言と解釈する
となっていて, 今の場合この仕様が適用されています. 実際, メッセージでも
「プロトタイプされている関数が呼び出されませんでした (変数の定義が意図されていますか?)」
と書いてありますよね. これは, 「ひょっとすると変数を定義しようとしているのかもしれないけど, (C++ の規格に従って) 関数のプロトタイプ宣言と解釈しましたよ」ってことも意味します.

Hoge hoge(Function());
のどこが「関数の宣言」なのかと疑問に思うかもしれませんが, これは実は
Hoge hoge(Function (*foo)());
と同じ意味になります. 関数の引数においては「関数」と「関数へのポインタ」が同一視されること, また「引数の名前は書かなくていい」ことに注意してください.

どう見ても宣言ととれない場合, たとえば
Hoge hoge(Function(1));

Hoge hoge(function);
では「変数の定義」と解釈されています.

ということで,
Hoge hoge = Hoge(Function());
のように定義することになります.
    • good
    • 0
この回答へのお礼

>定義とも宣言とも取れるものはすべて宣言と解釈する
なるほどそういうことですか。素早く的確な解答ありがとうございました。
大変わかりやすかったです。
Hoge hoge = Hoge(Function());
と書くことで、期待通りの動作をしました。

ただこちらはコピーコンストラクタが呼ばれるようで、
コピー禁止している場合にはできないようですね。

お礼日時:2011/03/16 18:03

テンプレートは関係ありません.


・コンストラクタに Function() を渡す Hoge型の変数 hoge の定義

・「引数をとらないで Function を返す関数 (へのポインタ)」を引数として Hoge を返す関数 hoge の宣言
が衝突しているため後者をとっています.

C++ の規格を確認してください.
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!