2014-02-22-01-ファンクタの悩み - 3 - project-enigma

2014-02-22-01-ファンクタの悩み - 3

>> Site top >> weblog >> 月別アーカイブ >> 2014年02月のlog >> 2014-02-22-01-ファンクタの悩み - 3

最終更新日付:2014/02/22 23:50:00


ファンクタの悩み - 3

2014 年 02 月 22 日

前回からの続き。

話を戻そう。そもそも、ファンクタを値渡ししなければならない理由とは何か。それをしないと、どういう局面で STL と挙動が異なってしまうのか。今回はそれについて。

こういうのは例を挙げるのが手っ取り早い。以下は、unary_function と for_each を使った簡単なコードだ。

#include <iostream>
#include <functional>
#include <algorithm>


struct Foo : std::unary_function<int, int> {
    int acc;
    Foo( ) : acc( 0 ) { };
    Foo( const Foo& other ) : acc( other.acc ) { };
    int operator()( int arg ) {
        acc += arg;
        return arg;
    };
};

int main( void ) {

    int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    Foo foo1;
    Foo foo2 = std::for_each( arr, arr + 10, foo1 );

    std::cout << "foo1.acc = " << foo1.acc << std::endl;
    std::cout << "foo2.acc = " << foo2.acc << std::endl;

    return 0;
}

ローカルに作成した foo1 オブジェクトを、for_each のパラメータとして渡し、返ってきたオブジェクトを foo2 で受け取っている。クラス Foo は呼び出される度にメンバ変数に値を加算していく。

で、このプログラムの出力はどうなるだろうか。foo1.acc は 0 で、foo2.acc は 55 だ。状態を持つファンクタを値渡ししているから、foo1 は変化しない。それが STL の挙動だ。では、現状ではファンクタの値渡しをやっていない CL-STL では対応するコードはどう動くだろうか。以下は、ファンクタクラスの仕組みを導入しつつ、for-each アルゴリズムがまだ functor-copy をしていない実装に対して書いたコードだ。

(defclass foo ()
  ((acc :type     :fixnum
        :initform 0
        :initarg  :acc
        :accessor foo-acc)))

(defmethod stl:functor-copy ((func foo))
  (make-instance 'foo :acc (foo-acc func)))

(defmethod stl:unary-function ((func foo) arg)
  (incf (foo-acc func) arg)
  arg)


(let* ((arr (stl:new-array-with-values 1 2 3 4 5 6 7 8 9 10))
       (foo1 (make-instance 'foo))
       (foo2 (stl:for-each (stl:begin arr) (stl:end arr) foo1)))
  (format t "foo1.acc = ~A.~%" (foo-acc foo1))
  (format t "foo2.acc = ~A.~%" (foo-acc foo2)))

stl:for-each がまだ functor-copy をしていないので、for-each 内部で functor-invoke されるファンクタオブジェクトは foo1 そのものだ。そして、それが(C++ の言い方ではポインタ渡しによって)foo2 に返される。だから上記の コードにおいて、foo1 と foo2 は eq になる。つまり、上記のコードでは foo のインスタンスは1つしか作成されない。まぁそれ自体も問題だが、結果として foo1 も foo2 も acc は 55 になる。くどいようだがインスタンスは1つなのだから「両方とも」という言い方自体がおかしいのだが。

まぁ、これがファンクタを値渡ししなければならない理由だ。正直なところを書いてしまえば、自分でも「挙動が違うなら違うで、別にいいけどなぁ」と思わないこともない。しかし、「要らざる非互換性は避ける」のだ。今後 C++(というか STL )のコードを Common Lisp に移すことがどれくらいあるのかわからないが、やる必要に迫られた時にはできるだけ機械的に作業したいからね。

 

コメント

このページにコメントする

 

このページのタグ

Page tag : STLとその移植

 

 


Copyright(C) 2005-2017 project-enigma.
Generated by CL-PREFAB.