不要试图获得仿函数的状态,或者讲不要试图通过仿函数来确定仿函数的某个值或状态
试图从运用了仿函数的算法中获得数据或者结果的方式有两种,分别是显式写出传递的类型参数,使用引用。如下面的:
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中仿函数的状态变化。
试图从运用了仿函数的算法中获得数据或者结果的方式有两种,分别是显式写出传递的类型参数,使用引用。如下面的:
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中仿函数的状态变化。