C++擬似関数/関数ポインタ/閉パッケージlambda

5916 ワード

C++11で新たに導入されたlambda式について前編で紹介した(C++は閉パッケージの実現をサポートしています)、今lambdaの出現が私たちのプログラミング習慣に与える影響を見てみましょう.結局、C++11は10年の試練を経て、140の新しいfeatureを出して、私たちのprogramming idiomに深い影響を及ぼしています.まずlambdaの出現が模倣関数に与える影響を見てみましょう.1)模倣関数
wikipediaの定義:
A function object, also called a functor, functional, or functionoid, is a computer programming construct allowing an object to be invoked or called like it was an ordinary function, usually with the same syntax.
Functor/Function Object翻訳すると擬似関数になります.これは、()演算子を再ロードして関数をシミュレートするクラスです.つまり、関数ではありません(したがって、シミュレーション関数は適切に翻訳されています).()演算子が再ロードされているため、関数を呼び出すように呼び出すことができます.STLではFunction Objectが多く用いられており,あらかじめ定義されたFunction Objectも多く提供されている.やはりvectorから例を繰り返します.
class PrintInt
{
public:
    void operator()(int elem) const
    {
        std::cout< v;
for_each(v.begin(),v.end(), PrintInt()); 

//C++ 11 lambda stype
for_each(begin(v), end(v), [](int n){ cout<< n <

擬似関数の利点:
1.擬似関数はオブジェクトであり、メンバー関数とメンバー変数、すなわち擬似関数所有状態(states)2を持つことができる.各シミュレーション関数には独自のタイプがある.シミュレーション関数は通常、一般関数よりも速い(多くの情報コンパイル期間決定)
まずは簡単にfor_eachのソース:
// TEMPLATE FUNCTION for_each
template inline
    _Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func)
    {    // perform function for each element

    _DEBUG_RANGE(_First, _Last);
    _DEBUG_POINTER(_Func);
    _CHECKED_BASE_TYPE(_InIt) _ChkFirst(_CHECKED_BASE(_First));
    _CHECKED_BASE_TYPE(_InIt) _ChkLast(_CHECKED_BASE(_Last));
    for (; _ChkFirst != _ChkLast; ++_ChkFirst)
        _Func(*_ChkFirst);
    return (_Func);
    }
    
//effective STL 
template< typename InputIterator, typename Function >
Function for_each( InputIterator beg, InputIterator end, Function f ) {
    while ( beg != end )
        f( *beg++ );
}
簡単に言えばfor_eachはテンプレート関数であり、forループ文をカプセル化し、前の2つのパラメータは反復器であり、3番目のパラメータは1つの関数ポインタ(または擬似関数)を使用し、その機能は各反復器が指す値に対して擬似関数を呼び出すことである.
STLは、演算子(plus,minus,multiplies,divides,modulusおよびnegate)、算術比較(equal_to,not_equal_to,greater,less,greater_equalおよびless_equal)、および論理操作(logical_and,logical_orおよびlogical_not)を含む多くの異なる事前定義された関数オブジェクトを含む.これにより、新しい関数オブジェクトを手動で書くのではなく、これらの関数オブジェクトを使用してかなり複雑な操作を組み合わせることができます.
もちろん,vectorのみを遍歴して出力するために,より簡単な方法を用いることができるために,我々の最初の例はシミュレーション関数の使い方を説明するためだけである.
copy(v.begin(), v.end(), std::ostream_iterator(std::cout," "));

実はlambdaを通じて、多くのSTLのシミュレーション関数は使わなくてもいいです.しかし、車輪がある以上、これらのシミュレーション関数を理解したり使用したりすれば、コードをより美しく、効率的に書くことができるに違いありません.
std::negate()を使用して配列要素の反転を行います.
transform( vect.begin(), vect.end(), vect.begin(), std::negate() ); 

// TEMPLATE STRUCT negate 
template   struct negate         : public unary_function<_ty _ty="">    
 { // functor for unary operator-     
   _Ty operator()(const _Ty& _Left) const      
     { // apply operator- to operand       
       return (-_Left);      
     } 
};

コンテナ内の5未満のすべての要素を削除します.
auto iter = std::remove_if( ivec.begin(), ivec.end(), std::bind2nd( std::less(), 5 ) );
ivec.erase( iter, ivec.end() );

std::lessについて
// TEMPLATE STRUCT less
template
    struct less
        : public binary_function<_ty _ty="" bool="">
    { // functor for operator<
    bool operator()(const _Ty& _Left, const _Ty& _Right) const
        { // apply operator< to operands
        return (_Left < _Right);
        }
    };

bind 2 ndの実現については,実現過程にもシミュレーション関数の運用がある.
// TEMPLATE FUNCTION bind2nd
template inline
    binder2nd<_fn2> bind2nd(const _Fn2& _Func, const _Ty& _Right)
    { // return a binder2nd functor adapter

    typename _Fn2::second_argument_type _Val(_Right);
    return (std::binder2nd<_fn2>(_Func, _Val));
    }

// TEMPLATE CLASS binder2nd
template
    class binder2nd
        : public unary_function
    { // functor adapter _Func(left, stored)
public:
    typedef unary_function _Base;
    typedef typename _Base::argument_type argument_type;
    typedef typename _Base::result_type result_type;

    binder2nd(const _Fn2& _Func,
        const typename _Fn2::second_argument_type& _Right)
        : op(_Func), value(_Right)
        { // construct from functor and right operand

        }

    result_type operator()(const argument_type& _Left) const
        { // apply functor to operands

        return (op(_Left, value));
        }

    result_type operator()(argument_type& _Left) const
        { // apply functor to operands

        return (op(_Left, value));
        }

protected:
    _Fn2 op; // the functor to apply
    typename _Fn2::second_argument_type value; // the right operand
};

以上のように,STLに内蔵された擬似関数は強力であるため,熟知すれば利用できる.そうでなければ、lambdaを使うのもいい選択ですが、結局、優雅なコードを書くことができます.
細分化しなければならない場合、lambdaはいつシミュレーション関数に代わることができますか?私たちはlambdaの制限から話す必要があります.この点はlambdaのスナップリストの制限によるものが大きい.現行のC++11規格では、スナップリストは親の役割ドメインの自動変数のみをスナップできます.たとえば次の例です.
 int d = 0;
 void test()
 {
    auto ill_lambda = [d]{};
 }
g++はコンパイルされますが、warningがあります.C++11の基準に厳格に従っているコンパイラの中には、直接エラーが発生します.シミュレーション関数には二次制限はありません.
簡単にまとめると、シミュレーション関数の代わりにlambdaを使用するには、いくつかの条件を満たす必要があります.
a)は、局所的な役割ドメインに限定されて使用されるコードロジックである.
b)これらのコードロジックはパラメータとして伝達する必要がある.
Lambdaが設計された主な目的の一つは,擬似関数の使用を簡略化することであり,文法は「典型的なC++」のように見えないが,熟知するとプログラマーは簡単な,その場での,状態付き関数定義を正確に記述することができる.もちろん、グローバル共有のコードロジックが必要な場合は、関数(ステータスなし)または擬似関数(ステータスあり)でカプセル化する必要があります.
参照:
http://blog.chinaunix.net/uid-20384806-id-1954378.html