c++ - CRTP:表达式模板的编译器相关问题

标签 c++ crtp expression-templates

我遇到了以下代码(存储在 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++ 的新手,我花了很多时间寻找错误但没有任何结果,我想问问更有经验的人的意见,以了解这个问题是否是由于我引入的某些错误引起的(因为我expect) 或编译器错误。

编辑: 在 Mike Seymour 的回答之后,我按现在的样子更改了代码。现在我没有收到编译器警告,但我仍然得到与以前相同的行为(具有相同的 valgrind 响应)。有人试过用 Intel 编译吗?

编辑: 我试图编译 Expression Templates 中的代码维基百科的页面。我获得了与我提供的示例完全相同的行为。

编辑: 我进一步调查了这个问题,似乎用 Intel icpc the operator

编译
operator const Derived&() const {
    return static_cast< const Derived& >(*this);
  }

递归调用自身。我发现的一种解决方法是用一种方法替换此运算符:

const Derived& get_ref() const {
    return static_cast< const Derived& >(*this);
  }

并相应地修改各个类的构造函数。任何人都可以告诉这两种行为中哪一种是正确的可能指向标准来解释这一点?

最佳答案

您应该始终启用编译器警告;他们经常可以发现细微的问题。在这种情况下:

g++ -Wall -Wextra test.cpp
test.cpp: In member function ‘const typename AlgebraicVectorExpression<AlgebraicVectorSum<T1, T2> >::ValueType& AlgebraicVectorSum<T1, T2>::operator[](typename AlgebraicVectorExpression<AlgebraicVectorSum<T1, T2> >::SizeType) const [with T1 = AlgebraicVector, T2 = AlgebraicVector]’:
test.cpp:90:   instantiated from ‘AlgebraicVector::AlgebraicVector(const AlgebraicVectorExpression<T1>&) [with T = AlgebraicVectorSum<AlgebraicVector, AlgebraicVector>]’
test.cpp:103:   instantiated from here
test.cpp:52: warning: returning reference to temporary

这告诉你问题所在:

const ValueType& operator[](SizeType ii) const {
    return (a_[ii] + b_[ii]);
}

表达式的结果是一个临时的,在该行的末尾销毁,因此该函数返回一个对不存在的对象的悬空引用。此运算符必须改为按值返回,并且您不应实现非 const 重载,因为没有要修改的值。

关于c++ - CRTP:表达式模板的编译器相关问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9738975/

相关文章:

c++ - 如何从可变模板参数的嵌套类派生?

c++ - 避免需要使用表达式模板进行#define

c++ - 为什么游戏不使用数学表达式模板?

c++ - Linux 使 : Need to rebuild a text file when makefile changes

c++ - 引用是否改变了引用对象的状态

c++ - 您可以在 CRTP 方法中对派生类使用约束吗?

c++ - CRTP 派生类在使用继承的赋值运算符后丢失其成员和段错误?

c++ - 防止表达式模板绑定(bind)到右值引用

c++ - 更改 STL 映射的赋值

c++ - v8 C++ API : pass non-English strings from JavaScript to c++