Search code examples
c++eigeneigen3

Is returning Eigen::Ref legit?


Eigen's doc explain how Eigen::Ref can be used as function parameters. Is it a good idea to use it as function return values too?

For example

class XyTracker : public XyListener {
 public:
  XyTracker() = default;
  XyTracker(XyTracker&&) = default;
  XyTracker& operator=(XyTracker&&) = default;

  const Eigen::Ref<const Eigen::ArrayXXd> Xs() const;
  const Eigen::Ref<const Eigen::ArrayXd> Ys() const;

  // XyListener
  void OnXy(const Eigen::VectorXd& x, double y) final;

 private:
  Eigen::ArrayXXd xs_;
  Eigen::ArrayXd ys_;
  Eigen::Index n_ = 0;
};

inline const Eigen::Ref<const Eigen::ArrayXXd> XyTracker::Xs() const {
  return xs_.topRows(n_);
}

inline const Eigen::Ref<const Eigen::ArrayXd> XyTracker::Ys() const {
  return ys_.head(n_);
}


Solution

  • As was already noted in the comments, there is nothing wrong with returning Ref objects, as long as you make sure that the object you are referring to is still valid at the time you are using the reference.

    Simplified example:

    struct A {
        ArrayXXd xs;
        Ref<const ArrayXXd> Xs() const {
            return xs.topRows(5);  // works (when used properly)
        }
        Ref<const ArrayXXd> NotWorking() const {
            ArrayXXd foo = 2*xs;
            return foo.topRows(5); // compiles, but will give U.B.
    };
    
    
    // usage:
    A a;
    a.xs.setRandom(10,10);
    
    Ref<const ArrayXXd> r1 = a.Xs();
    // fine to use r1 here
    a.xs.setRandom(20,20);
    // from here on r1 will point to non-existing memory
    // i.e., using r1 will be U.B.
    

    The U.B. (undefined behavior) examples are all essentially use-after-free, which unfortunately are barely detected by compilers, so you need to be somewhat careful when working with Eigen::Ref. UB can mean that your unit-tests work, although your code is bad, but suddenly in production you'll get a seg-fault ...