Reference becomes dangling in expression template

1

I wrote an expression template code, but I had an error.
And the following is just a minimal reproducible example.

#include <iostream>

template<typename Derived>
class VecExp {
 public:
  double operator[](int i) const {return static_cast<Derived const&>(*this)[i];}
};

template<int n>
class Vector : public VecExp<Vector<n>> {
 public:

  template<int m>
  Vector(const double(&arr)[m]) {
    static_assert(m==n, "");
    for(int i=0; i<m; ++i) data[i] = arr[i];
  }

  template<typename E>
  Vector(const VecExp<E>& exp) {
    for(int i=0; i<n; ++i) data[i] = exp[i];
  }

  double operator[](int i) const {std::cout<<this<<std::endl; return data[i];}
  double& operator[](int i) {return data[i];}
  double data[n];
};

template<typename E1, typename E2>
class VectorSum : public VecExp<VectorSum<E1, E2>>{
  E1 const& lhs;
  E2 const& rhs;
 public:

  VectorSum(E1 const& lhs, E2 const& rhs) : lhs(lhs), rhs(rhs) {
//    std::cout << &(this->lhs) << ", " << &(this->rhs) << std::endl;
  }

  double operator[](int i) const {return lhs[i] + rhs[i];}
};

template<typename E1, typename E2>
VectorSum<E1, E2>
operator + (VecExp<E1> const& lhs, VecExp<E2> const& rhs) {
  return VectorSum<E1, E2>(*static_cast<const E1*>(&lhs), *static_cast<const E2*>(&rhs));
}

int main() {
  Vector<16> v({1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16});
  std::cout << &v << std::endl;
  const auto sum = v + v + v + v;
  Vector<16> v2 = sum; // error at here

  std::cout << sum[0] << std::endl;

  return 0;
}

When I try to use VectorSum<...>(sum in main()) that adds more than 3 Vectors, the following error happens in a release build.

Exception 0xc0000005 encountered at address 0x7ff76f8a1189: Access violation reading location 0x00000000

So I attached the pointer printer like above, and then it says

00000067C1EFF920
0000000000000000

Process finished with exit code -1073741819 (0xC0000005)

If I attach more printers(i.e: in operator + or VecSum ctor, ...), then suddenly everything works fine.
Can anyone please tell me why is this error happening?

Used CLion on Windows, Visual Studio community 2019 installed. toolchain

c++
templates
reference
asked on Stack Overflow Feb 8, 2021 by 김선달 • edited Feb 8, 2021 by 김선달

1 Answer

5

v + v is an expression of type VectorSum<Vector<16>, Vector<16>>.

v + v + v is an expression of type VectorSum<VectorSum<Vector<16>, Vector<16>>, Vector<16>>, where the type for the first template parameter is significant:

   VectorSum<VectorSum<Vector<16>, Vector<16>>, Vector<16>>
//           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (B)
// ^^^^^^^^^ (A)

The object associated with this specialization, A, stores a const reference of type B const&, but once the expression v + v + v expires so does the lifetime of the sub-expression v + v, meaning you are storing a dangling reference in the expression object (A) (as the data member lhs).

The reason you do not see this problem when just storing the expression template for the expression v + v is that both the lhs and rhs data members of the associated specialization object refers to the object v whose lifetime extends to the end of main().

Finally, be careful when storing expression templates in intermediate variables using placeholder types such as auto, as it will 1) be likely to yield lifetime issues such as the one above, and 2) is likely to be unexpected to the casual reader, as one could expect (compare with std::vector<bool>::operator[])) auto = v + v to result in decltype(v) (namely a Vector<16>) rather than a proxy type that should for all means be hidden from the client.

answered on Stack Overflow Feb 8, 2021 by dfrib • edited Feb 8, 2021 by dfrib

User contributions licensed under CC BY-SA 3.0