template <typename Container>
class back_insert_iterator : public eastl::iterator <EASTL_ITC_NS:: output_iterator_tag, void , void, void, void >
{
public:
typedef Container container_type;
typedef typename Container:: const_reference const_reference ;
protected:
Container& container ;
public:
explicit back_insert_iterator (Container& x)
: container(x ) { }
back_insert_iterator& operator =(const_reference value)
{ container.push_back (value); return *this ; }
back_insert_iterator& operator *()
{ return *this ; }
back_insert_iterator& operator ++()
{ return *this ; } // This is by design.
back_insert_iterator operator ++(int)
{ return *this ; } // This is by design.
};
template <typename Container>
back_insert_iterator<Container > back_inserter( Container& x )
{
return back_insert_iterator <Container>( x);
}
2. 把相应代码copy到CPP代码里面,放进自己的namespace test,然后再使用新版本的test::back_inserter,居然就没有warning了。两者代码完全一样。
eastl::copy(behaviors.begin(), behaviors.end(), test::back_inserter(m_kBehaviors));
3. 怀疑是全局的compiler状态影响了编译结果,既然是 warning c4512,是不是我们在头文件里面定义过disable这个warning,所以导致test::back_inserter编译时刻的状态和eastl::back_inserter的编译状态不同呢?于是在test::back_inserter后面,测试test::back_inserter的前面,加上了#pragma warning(default: 4512),恢复编译器的warning 4512状态。我的原意是希望在编译到test::back_inserter(m_kBehavior)的时候使用新的编译warning状态,这样就会报错了。 居然还是不报错。
4. 想了一下,觉得template是在编译到test::back_inserter(m_kBehavior)的时候报错,但其实在之前编译模版定义的时候已经有disable warning 4512的状态了。所以这个#pragma warning(default: 4512)应该加在namespace test之前,这样test::back_inserter模版定义带有disable warning 的buff。试了一下果然有效,又开始报出warning了。
结论1: #pragma的状态,在模版定义时生效,而不是在模版实例化时生效。想要改变模版中的编译状态,就要在定义模版前pragma
想想也符合逻辑。。。我的猜想是,一般实例化template的时候还是需要回去看template的定义的,而此时的编译器状态要被恢复到那时候的状态了,合情合理
5. 感觉问题搞定了,就是因为C4512被disable了,所以test::back_inserter不会报错,而编译eastl::back_inserter的时候,这个warning还没有被disable,所以报出warning
6. 突然想到,为啥std::back_inserter一直不报warning呢?连忙开了一个空项目,把eastl::back_inserter和std::back_inserter代码全部都抽出来放进去,再做试验。std怎么都不错,eastl就会出问题。如果加上#pragma warning ( disable: 4512),eastl也会shut up了。中间还绕了一些弯路,怀疑std的stl是不是偷偷在header里面disable了warning,事实证明我想多了。
7. 这不是问题的关键,关键是这个warning本身,一定是std和eastl实现上有一些不同。仔细比较两者的代码,发现了关键的不同点:
为了实现back_inserter功能,两者都需要保证可以访问原有容器,存放原有容器的方法有不同。 EASTL里面用了reference,STD stl里面用了Pointer。
而编译器会为这个类隐式产生拷贝构造函数assignment operator,在EASTL里面由于是引用,所以编译器无法产生拷贝构造函数,warning里面抱怨的就是这个情况。见Effective C++, Item 5.
From Effective C++, Item 5
如果你自己不声明一个拷贝构造函数,一个拷贝赋值运算符和一个析构函数,编译器就会为这些东西声明一个它自己的版本。而且,如果你自己连一个构造函数都没有声明,编译器就会为你声明一个缺省构造函数。
面对这个难题,c++ 拒绝编译器产生代码。如果你希望一个包含引用成员的类支持赋值,你必须自己定义拷贝赋值运算符
结论2:EASTL的back_inserter_iterator实现不够好,需要把引用换成指针避免warning
8. 额外问题:问题已经解决。但是引申出来的问题是,back_insert_iterator里面不是已经有operator=了吗,为啥编译器还要生成一个operator=,以至于报出warning。简单研究了一下,答案很简单,operator=的prototype不同。正规的operator=应该是这样的
back_insert_iterator < Container>& operator =(const back_insert_iterator& value )
但现有的类里面是这样的,根本不是同一个东西。。。
back_insert_iterator & operator =( const_reference value )
总结:查这个问题走了很多的弯路,总是怀疑是自己的问题,不从EASTL本身去查原因,去查编译器状态,导致开始走错了方向。