Я столкнулся с проблемой, зависящей от компилятора, со следующим кодом (хранится в 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;
}
На самом деле, когда я компилирую это:
$ 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
Я получаю:
$ ./a.out
0
2
4
6
8
10
12
14
16
18
что является ожидаемым поведением. Когда я использую icpc:
$ icpc --version
icpc (ICC) 12.1.0 20110811
Copyright (C) 1985-2011 Intel Corporation. All rights reserved.
$ icpc -g -O0 crtp.cc
Вместо этого я получаю Segmentation fault
. Запуск
valgrind --tool=memcheck ./a.out
указывает на строку 29 в источниках
AlgebraicVectorExpression<AlgebraicVector>::operator AlgebraicVector const&() const (crtp.cc:29)
Поскольку я довольно новичок в C ++, и я потратил довольно много времени на поиск ошибки без какого-либо результата, я хотел бы спросить мнение кого-то более опытного, чтобы понять, вызвана ли эта проблема какой-то ошибкой, которую я представил (поскольку я ожидать) или к ошибке компилятора.
Редактировать :
Я изменил код, как сейчас, после ответа от Майка Сеймура. Теперь я не получаю предупреждений компилятора, но я все равно получаю то же поведение, что и раньше (с тем же ответом valgrind). Кто-нибудь пробовал скомпилировать с Intel?
Редактировать :
Я попытался скомпилировать код на странице Expression Templates *1029* в Википедии. Я получил то же самое поведение, что и в приведенном мной примере.
Редактировать :
Я исследовал проблему дальше, и кажется, что компиляция с Intel icpc
оператор
operator const Derived&() const {
return static_cast< const Derived& >(*this);
}
рекурсивно вызывает себя. Один из обходных путей, который я нашел, состоит в том, чтобы заменить этот оператор методом:
const Derived& get_ref() const {
return static_cast< const Derived& >(*this);
}
и соответственно измените конструкторы различных классов. Кто-нибудь может сказать, какое из этих двух поведений является правильным, возможно, указывая на стандарт, чтобы объяснить это?