Using coeffRef with const CwiseUnaryView - Failing when multiplying two CWiseUnaryViews

Viewed 233

I'm having some trouble when using coeffRef() with a CWiseUnaryView function, but only when the function is declared as const

Reproducible example:

#include <Eigen/Core>

struct dummy_Op {
  EIGEN_EMPTY_STRUCT_CTOR(dummy_Op)
  EIGEN_DEVICE_FUNC
  EIGEN_STRONG_INLINE const double& 
    operator()(const double &v) const { return v; }
  EIGEN_DEVICE_FUNC
  EIGEN_STRONG_INLINE double& 
    operator()(double &v) const { return v; }
};

void foo(Eigen::MatrixXd &out)
{
    //Compiles
    Eigen::CwiseUnaryView<dummy_Op, Eigen::MatrixXd> view(out);
    view.coeffRef(0,0);

    //Doesn't Compile
    const Eigen::CwiseUnaryView<dummy_Op, Eigen::MatrixXd> const_view(out);
    const_view.coeffRef(0,0);
}

Returns:

<source>: In function 'void foo(Eigen::MatrixXd&)':
<source>:21:28: error: passing 'const Eigen::CwiseUnaryView<dummy_Op, 
Eigen::Matrix<double, -1, -1> >' as 'this' argument discards qualifiers 
[-fpermissive]

     const_view.coeffRef(0,0);

                            ^

In file included from /opt/compiler-explorer/libs/eigen/v3.3.4/Eigen/Core:413,
                 from <source>:1:
/opt/compiler-explorer/libs/eigen/v3.3.4/Eigen/src/Core/DenseCoeffsBase.h:340:33: note:   
in call to 'Eigen::DenseCoeffsBase<Derived, 1>::Scalar& 
Eigen::DenseCoeffsBase<Derived, 1>::coeffRef(Eigen::Index, Eigen::Index) 
[with Derived = Eigen::CwiseUnaryView<dummy_Op, Eigen::Matrix<double, 
-1, -1> >; Eigen::DenseCoeffsBase<Derived, 1>::Scalar = double; Eigen::Index = long int]'

     EIGEN_STRONG_INLINE Scalar& coeffRef(Index row, Index col)

                                 ^~~~~~~~

Compiler returned: 1

Compiler explorer: https://godbolt.org/z/kPHPuC

The side-effect of this, is that the multiplication of two (non-const) CWiseUnaryViews also fails, see example here: https://godbolt.org/z/JYQb3d

2 Answers

The bottom line is that you're calling a non-const method of a constant instance. The (first) coeffRef that is being called is the one (and only) in DenseCoeffsBase.h (DenseCoeffsBase<Derived, WriteAccessors>), which is not const qualified. The DenseCoeffsBase<Derived, ReadOnlyAccessors> class does not have a coeffRef method. You can get around this error (and get a warning) if you enable the -fpermissive compiler flag.

In the dense case, you probably want to use the operator()(Index, Index) method anyway, which does have a const qualified version. I just noticed the documentation explicitly says to use that method anyway, even for the non-const version. This is obviously not going to return a const reference, but at least in your example as a double, it shouldn't matter too much.

CwiseUnaryView is intended to be used for L-value like expression, e.g.,

MatrixXcd A;
A.real() = something; // `A.real()` is writable

If you want to apply an element-wise functor and use it as an R-value, you should use CwiseUnaryOp instead:

void foo(Eigen::MatrixXd &out)
{
    Eigen::CwiseUnaryOp<dummy_Op, Eigen::MatrixXd> view1(out);
    // shorter:
    auto view2 = out.unaryExpr(dummy_Op());

    Eigen::MatrixXd result = view1 * view2;
    // or directly write: out.unaryExpr(dummy_Op()) * out.unaryExpr(dummy_Op());
}
Related