I suffered in a compiler related issue with the following code (stored in crtp.cc):
#include <vector> #include <cassert> #include <iostream> template < class Derived > class AlgebraicVectorExpression { public: typedef std::vector<double>::size_type SizeType; typedef std::vector<double>::value_type ValueType; typedef std::vector<double>::reference ReferenceType; SizeType size() const { return static_cast<const Derived&>(*this).size(); } ValueType operator[](SizeType ii) const { return static_cast<const Derived&>(*this)[ii]; } operator Derived&() { return static_cast<Derived&>(*this); } operator const Derived&() const { return static_cast< const Derived& >(*this); } }; template< class T1, class T2> class AlgebraicVectorSum : public AlgebraicVectorExpression< AlgebraicVectorSum<T1,T2> > { const T1 & a_; const T2 & b_; typedef typename AlgebraicVectorExpression< AlgebraicVectorSum<T1,T2> >::SizeType SizeType; typedef typename AlgebraicVectorExpression< AlgebraicVectorSum<T1,T2> >::ValueType ValueType; public: AlgebraicVectorSum(const AlgebraicVectorExpression<T1>& a, const AlgebraicVectorExpression<T1>& b) : a_(a), b_(b) { assert(a_.size() == b_.size()); } SizeType size() const { return a_.size(); } ValueType operator[](SizeType ii) const { return (a_[ii] + b_[ii]); } }; template< class T1, class T2> const AlgebraicVectorSum<T1,T2> operator+(const AlgebraicVectorExpression<T1>& a, const AlgebraicVectorExpression<T2>& b) { return AlgebraicVectorSum<T1,T2>(a,b); } class AlgebraicVector : public AlgebraicVectorExpression<AlgebraicVector>{ std::vector<double> data_; public: SizeType size() const { return data_.size(); } ValueType operator[](SizeType ii) const { return data_[ii]; } ValueType& operator[](SizeType ii) { return data_[ii]; } AlgebraicVector(SizeType n) : data_(n,0.0) { }; template< class T> AlgebraicVector(const AlgebraicVectorExpression<T>& vec) { const T& v = vec; data_.resize(v.size()); for( SizeType idx = 0; idx != v.size(); ++idx) { data_[idx] = v[idx]; } } }; int main() { AlgebraicVector x(10); AlgebraicVector y(10); for (int ii = 0; ii != 10; ++ii) x[ii] = y[ii] = ii; AlgebraicVector z(10); z = x + y; for(int ii = 0; ii != 10; ++ii) std::cout << z[ii] << std::endl; return 0; }
In fact, when I compile it with:
$ g++ --version g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3 Copyright (C) 2009 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ g++ -O0 -g crtp.cc
I get:
$ ./a.out 0 2 4 6 8 10 12 14 16 18
what is the expected behavior. When I use icpc:
$ icpc --version icpc (ICC) 12.1.0 20110811 Copyright (C) 1985-2011 Intel Corporation. All rights reserved. $ icpc -g -O0 crtp.cc
I get instead of a Segmentation fault . Launch
valgrind
points to line 29 in the sources
AlgebraicVectorExpression<AlgebraicVector>::operator AlgebraicVector const&() const (crtp.cc:29)
Since I'm pretty new to C ++, and I spent a lot of time looking for an error without any result, I would like to ask the opinion of someone more experienced to understand if this problem is caused by some error that I introduced (since I expect) or to a compiler error.
Edit : I changed the code as it is now, after Mike Seymour answered. Now I don't get a compiler warning, but I still get the same behavior as before (with the same valgrind answer). Has anyone tried to compile with Intel?
Edit : I tried to compile the code on the Expression Templates page on Wikipedia. I got the exact same behavior as in the example below.
Edit : I investigated the problem further, and it seems that compiling with the Intel icpc operator
operator const Derived&() const { return static_cast< const Derived& >(*this); }
calls itself recursively. The workaround I found is to replace this statement with a method:
const Derived& get_ref() const { return static_cast< const Derived& >(*this); }
and accordingly modify the constructors of various classes. Can someone say which of these two actions is correct, perhaps pointing to a standard to explain this?