仿函数的状态获取的一些问题

 不要试图获得仿函数的状态,或者讲不要试图通过仿函数来确定仿函数的某个值或状态


试图从运用了仿函数的算法中获得数据或者结果的方式有两种,分别是显式写出传递的类型参数,使用引用。如下面的:

    list<int> coll;

    IntSequence seq(3);
   
    generate_n<back_insert_iterator<list<int> >,int,IntSequence&>(back_inserter(coll),4,seq);

或者使用for_each算法,返回一个仿函数的copy~:

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

    for_each(array,array+9,add1);

这是原生指针的情况。

    这两种情况,第二种限定了算法。

    对于第一种,有一个很大的问题,那就是不同编译环境其STL库的对应算法实现是不同的!这造成第一种情况在有些IDE中无法实现!

    #include <iostream>
    #include <string>
    #include <math.h>
    #include <stdlib.h>
    #include <fstream>
    #include <algorithm>
    #include <iterator>
    #include <list>
    using namespace std;

class IntSequence
{
private:
    int value;
   
public:
    IntSequence(int initialValue):value(initialValue)
    {

    }
    int operator()()
    {
        cout<<value+1<<endl;
        return value++;
    }

};


template<class T>
inline void PRINT_ELEM(const T&col,const char *op = "")
{
    T::const_iterator pos;            //使用const迭代器,表示不能修改元素
    std::cout<<op;
    for(pos = col.begin();pos!=col.end();++pos)
    {
        std::cout<<*pos<<' ';
    }
    std::cout<<endl;
}


int main()
{
    list<int> coll;

    IntSequence seq(3);
   
    generate_n<back_insert_iterator<list<int> >,int,IntSequence&>(back_inserter(coll),4,seq); //传引用方式
    PRINT_ELEM(coll,"print:\n");
    //generate_n(back_inserter(coll),4,seq);
    generate_n<back_insert_iterator<list<int> >,int,IntSequence&>(back_inserter(coll),4,seq);
    PRINT_ELEM(coll);
    system("pause");


    return 0;
}
这个情况下,你会发现在VS2010VS2012上,这个seq并没有按照我们想的那样,按引用传递,我们可以重用修改后的seq。

但是这个情况在codeblock上就完全不一样了!

这是为什么呢。如果我们自己实现IntSequence的拷贝构造函数,你就会发现如下问题

#include <iostream>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <fstream>
#include <algorithm>
#include <iterator>
#include <list>
using namespace std;

class IntSequence
{
private:
    int value;
   
public:
    IntSequence(int initialValue):value(initialValue)
    {

    }
    int operator()()
    {
        cout<<value+1<<endl;
        return value++;
    }
    IntSequence& operator=(const IntSequence &t)
    {
        cout<<"123";
        value = t.value;
    }
    IntSequence(IntSequence &t)
    {
        cout<<"456"<<endl;
        value = t.value;
        cout<<t.value<<endl;
    }
};
template<class T>
inline void PRINT_ELEM(const T&col,const char *op = "")
{
    T::const_iterator pos;            //使用const迭代器,表示不能修改元素
    std::cout<<op;
    for(pos = col.begin();pos!=col.end();++pos)
    {
        std::cout<<*pos<<' ';
    }
    std::cout<<endl;
}


int main()
{
    list<int> coll;

    IntSequence seq(3);
   
    generate_n<back_insert_iterator<list<int> >,int,IntSequence&>(back_inserter(coll),4,seq); //传引用方式
    PRINT_ELEM(coll,"print:\n");
    //generate_n(back_inserter(coll),4,seq);
    generate_n<back_insert_iterator<list<int> >,int,IntSequence&>(back_inserter(coll),4,seq);
    PRINT_ELEM(coll);
    system("pause");
 

    return 0;
}

你会发现,在这里多次调用了拷贝构造函数,这是为什么呢。
而当这段代码在codeblock上运行时,又没调用拷贝构造函数。

这是因为vs和codeblock上面的对STL的实现不同

首先,来看拷贝构造函数的作用
 
我们写这么一个类A
class A
{

public:
    A(A&t)
    {
        cout<<"nidaye"<<endl;
    }
    A(int t):a(t){}
    int a;
};
template <class T>
void func1(T t)
{
    t.a+=2;
}
template <class T>
void func(T t)
{
    t.a+=2;
    //return func1(t);
    //return func1<T&>(t);
}
int main()
{
  
    A t(3);
    func<A&>(t);
    cout<<t.a;
    return 0;
}
重要的代码我变色示意 ,首先是绿色的代码,如果我们不显式写出传递的是A&类型,结果是返回3和nidaye。
即调用了一次拷贝构造函数,因为是值传递

如果我们显式写出传递的是A&类型,结果就是5,因为我们是按引用传递。

对于红色的代码,如果我们不在func内修改,而是调用func1来修改,这时候返回的结果就是3和nidaye
这是为什么呢

因为调用内部函数的时候是传递的是值传递,而并没有吧引用传递,在调用func1的时候,会调用拷贝构造函数

那么如果想多次传递仍保证是值传递,那么可以写成return func1<T&>(t),这样可以显式的传递一个引用。

另一种方式,就是传递类型的时候不用显式传递,但是func的参数是&t即T*类型,然后需要修改蓝色部分为 t->a+=2

也可以避免值传递。

接下来来看VS和codeblock对

以generate_n算法为例。

这是codeblock的实现
  template<typename _OutputIterator, typename _Size, typename _Generator>
    _OutputIterator
    generate_n(_OutputIterator __first, _Size __n, _Generator __gen)
    {
      // concept requirements
      __glibcxx_function_requires(_OutputIteratorConcept<_OutputIterator,
            // "the type returned by a _Generator"
            __typeof__(__gen())>)

      for (__decltype(__n + 0) __niter = __n;
       __niter > 0; --__niter, ++__first)
    *__first = __gen();
      return __first;
    }
可以看到,传入的引用立刻被使用为__gen()用于产生一个插入值,然后通过*来获得back_inserter本身,然后通过重载的=运算符中实现插入
 

在VS中的generate_n的实现,首先调用
template<class _OutIt,
    class _Diff,
    class _Fn0> inline
    _OutIt generate_n(_OutIt _Dest, _Diff _Count, _Fn0 _Func)
    {    // replace [_Dest, _Dest + _Count) with _Func()
    _DEBUG_POINTER(_Dest);
    _DEBUG_POINTER(_Func);
    return (_Generate_n(_Dest, _Count, _Func,
        _Is_checked(_Dest)));
    }
然后调用
template<class _OutIt,
    class _Diff,
    class _Fn0> inline
    _OutIt _Generate_n(_OutIt _Dest, _Diff _Count, _Fn0 _Func,
        true_type)
    {    // replace [_Dest, _Dest + _Count) with _Func(), checked dest
    return (_Generate_n1(_Dest, _Count, _Func,
        _Iter_cat(_Dest)));
    }

然后调用
template<class _OutIt,
    class _Diff,
    class _Fn0> inline
    _OutIt _Generate_n1(_OutIt _Dest, _Diff _Count, _Fn0 _Func,
        output_iterator_tag)
    {    // replace [_Dest, _Dest + _Count), arbitrary iterators
    return (_Generate_n(_Dest, _Count, _Func));
    }
然后调用
template<class _OutIt,
    class _Diff,
    class _Fn0> inline
    _OutIt _Generate_n(_OutIt _Dest, _Diff _Count, _Fn0 _Func)
    {    // replace [_Dest, _Dest + _Count) with _Func()
    for (; 0 < _Count; --_Count, ++_Dest)
        *_Dest = _Func();
    return (_Dest);
    }
一共3次间接调用,并且没有显式传递引用参数,这表示即使开始我们使用显式的引用参数传递,在3次间接调用之后,也会调用3次拷贝构造函数,并且最终的实现使用的仅仅是一个原传入对象的副本。所以我们无法获得VS中仿函数的状态变化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值