Search code examples
c++templatesreferenceeigenc++20

Use of std::forward with Eigen::Ref objects


I have a functor Foo defined as follows:

struct Foo {
    template <typename _Vector>
    void operator()(const Eigen::Ref<const _Vector>&, const Eigen::Ref<const _Vector>&) {
        /* ... */
    }
};

I'd like to call it with either Eigen vectors, blocks, or Eigen::Ref, and _Vector can be any of Eigen vector types. This implementation doesn't work because you must call it exactly with Eigen::Ref<const _Vector> arguments -- and not, for instance, with vectors, or segments of vectors. A workaround I found is the following:

struct Foo {
    /* This works, but I'm not sure if it is the most correct solution. */
    template <typename T, typename U>
    void operator()(const T& x, const U& y) {
        Impl<typename T::PlainObject>(x, y);
    }

    /* This doesn't work, but I expected this to be the most correct solution. */
    // template <typename T, typename U>
    // void operator()(T&& x, U&& y) {
    //     Impl<typename T::PlainObject>(std::forward<T>(x), std::forward<U>(y));
    // }

    template <typename _Vector>
    void Impl(const Eigen::Ref<const _Vector>&, const Eigen::Ref<const _Vector>&) {
        /* ... */
    }
};

Here, I hide the implementation inside a function Impl and catch every argument with operator(); then, I deduce the right _Vector type from the member type PlainObject of the first argument (x). However, I expect the commented code to be more "correct" since it perfectly forwards the arguments, so that they are used by Impl being exactly as they were supposed to be. Is this assertion wrong? Unfortunately, it fails with the following error:

error: ‘Eigen::Ref<Eigen::Matrix<double, -1, 1> >&’ is not a class, struct, or union type
[build]   390 |         Impl<typename T::PlainObject>(std::forward<T>(x), std::forward<U>(y));
[build]       |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

It states that Eigen::Ref is not a class, struct, or union type; but it actually is a class. How can this error be solved?

P.S.: I must confess that I'm not 100% confident I understood when T&& and std::forward should be used. My understanding is that they give you exactly what you pass with no extra copies or implicit conversions.


Solution

  • error: ‘Eigen::Ref<Eigen::Matrix<double, -1, 1> >&’ is not a class, struct, or union type
                                                     ^
    

    You need to std::remove_reference to make the version with perfect forwarding valid:

    #include <type_traits>
    
    template <typename T, typename U>
    void operator()(T&& x, U&& y) {
         Impl<typename std::remove_reference_t<T>::PlainObject>(std::forward<T>(x),
                                                                std::forward<U>(y));
    }