関数オブジェクト呼び出し方


hi-ho  2011-06-01 23:54:51  No: 72694  IP: 192.*.*.*

お世話になります。
現在、C++を勉強しているのですが、関数オブジェクトの呼び出し方について
教えて頂けないでしょうか。

下記のサンプルコードの★でrandom_shuffleの第3引数に関数オブジェクト
を指定すると思っているのですが、なぜRandomクラスのインスタンスrを指定
しても関数オブジェクトを呼ぶことができるのでしょうか。

r ではなく Random() ではないのでしょうか。

どうかご教授をお願いできないでしょうか。
よろしくお願いします。



#include <iostream>
#include <algorithm>
#include <vector>

#include <cstdlib>
#include <ctime>

class Random
{
public:
  // コンストラクタ
  Random() {
    srand(static_cast<unsigned int>( time(NULL) ));
  }
  // 関数オブジェクト
  unsigned int operator() (unsigned int max) {
    double tmp = static_cast<double>( rand() ) / static_cast<double>(RAND_MAX);
    return static_cast<unsigned int>( tmp * max );
  }
};

int main()
{
  using namespace std;

  vector<int> nums;
  int i;
  for(i = 0; i < 10; i++) nums.push_back(i);

  Random r;
  random_shuffle( nums.begin(), nums.end(), r);  // ★
  for(i = 0; i < 10; i++)
    cout << nums[i] << endl;

  return 0;
}

編集 削除
επιστημη  URL  2011-06-02 05:41:21  No: 72695  IP: 192.*.*.*

> r ではなく Random() ではないのでしょうか。

おんなじやん。

編集 削除
仲澤@失業者  2011-06-02 17:35:24  No: 72696  IP: 192.*.*.*

>r ではなく Random() ではないのでしょうか。

Random r;
random_shuffle( nums.begin(), nums.end(), Random()); // ★

とした場合、
  Random r;
の行でわざわざ作ったRandomのインスタンス r が
使われずに無駄になりますよねぇ・・・もったいないですし(vv;)。

編集 削除
仲澤@失業者  2011-06-02 17:48:22  No: 72697  IP: 192.*.*.*

ちょっと不親切だったかもしれないので補足。
Random r;  // Randomのインスタンス r を作った
random_shuffle(
   nums.begin(),
   nums.end(),
   Random()); // rとは別のRandomのインスタンスをスタック上に作って
              // その参照を渡した
ということですね。

編集 削除
tetrapod  2011-06-03 18:08:37  No: 72698  IP: 192.*.*.*

提示例題は
http://www.geocities.jp/ky_webid/cpp/library/018.html

まあ答えは既に出ているとおり「同じ」なのだが、なぜ同じかを解説してみるテスト

関数オブジェクトという名称があまりにも適切すぎるため、
わかっている人間は言い換えようとも思わないわけだ。だが、
わからない人間には何のことだかさっぱりわからん・・・ということで。

関数オブジェクト=関数のように振舞うオブジェクト
≒関数のように振舞う変数
と書き換えると、ある程度わかりやすくなるかな?
(オブジェクトのうちの1つに変数がある)

関数のように振舞う「変数」だから、ごく普通に変数を作ることができる
int i; // のように int 型の変数を作るのと同じく
Random r; // のように「関数のように振舞う変数」を作っていい。
random_shuffle(..., r); のようにその変数を渡しても何の問題もない。

元発言者氏は(Random r; を除去して)random_shuffle(..., Random()); と書くべきではないか?
という疑問を持っているのだろう。この場合 r を再利用していないので、どっちでもいい。

--- 以下もうちょっと高度な解説

オブジェクトという単語/文言は抽象的過ぎてわかりにくい。
この単語を、ある観点から大きく2つに分類してみる。
「名前のあるオブジェクト」
「名前のないオブジェクト」(一時オブジェクト)

int i, j, k; と書くと変数 i j k が作られ、どれも [名前のあるオブジェクト] だ。
先ほどの例での Random r; も r という [名前のあるオブジェクト] を作っている。

k=i+j+1; という式を考えてみよう。
この式の中には [名前のないオブジェクト] が現れている。
i+j というの演算途中の一時的結果も int 型のオブジェクトなわけだ。
この [名前のないオブジェクト] が一時的に [コンストラクト] されている。
次に、この [名前のないオブジェクト] +1 という演算が行われる。
この演算結果もまた [別の、名前のないオブジェクト] なわけだ。
最後に [別の、名前のないオブジェクト] の値を k に代入し、
2つの [名前のないオブジェクト] 2つが [デストラクト] される。
というのが C++ 的考え方。

[名前のないオブジェクト] とは演算中に一時的に作られ、不必要になったとき即処分される [もの] 。
一時オブジェクトともいう。

[名前のあるオブジェクト] とは { 中で作ると対応する } まで存在する [もの] 。

([もの]=オブジェクトである。=変数 と考えてさほど間違いではない)

random_shuffle(..., Random()); と書くと、この Random() は一時オブジェクトの生成になる。
名前のない変数 Random() は 式文の終了の ; 到達時点でデストラクトされる。
つまり再利用することができない。
Random r; と書くと、これを括る } 到達まで r は存在し続けるので、
(その必要があれば) r を再利用できる。
という違いは、ある。

関数オブジェクトという文言を使うときはたいてい [一時オブジェクト] として使うので
多くのサンプルでは Random() のように使っているわけだ。

編集 削除