Search code examples
c++c++14

How to std::forward( *this )


because I want to overload all cv and reference qualifications of a member function, I wrote myself the following macro:

#define DEFINE_FUNCTION(sig , functionality) \
    sig & { functionality; } \
    sig && { functionality; } \
    sig const & { functionality; } \
    sig const && { functionality; } \
    sig volatile & { functionality; } \
    sig volatile && { functionality; } \
    sig const volatile & { functionality; } \
    sig const volatile && { functionality; }

Unfortunately, If I want to return *this in a forwarding manner (that is preserving all reference and cv-qualification of the this-pointer within the return type, it seems, I have to manually write all 8 of those overloads.

Now to my question: Is it possible to get the cv and reference qualified type of *this in a universal manner?

I have tried decltype(auto) as return type and

return std::forward<decltype(*this)>(*this);

but apparently, the resulting expression always resolves to an lvalue reference, even in case of a &&-qualified function.

Can you help me?


Solution

  • What you need is a friend.

    struct blah {
      decltype(auto) foo() & { return do_foo(*this); }
      decltype(auto) foo() && { return do_foo(std::move(*this)); }
      decltype(auto) foo() const& { return do_foo(*this); }
      decltype(auto) foo() const&& { return do_foo(std::move(*this)); }
      decltype(auto) foo() const volatile& { return do_foo(*this); }
      decltype(auto) foo() const volatile&& { return do_foo(std::move(*this)); }
      decltype(auto) foo() volatile& { return do_foo(*this); }
      decltype(auto) foo() volatile&& { return do_foo(std::move(*this)); }
    
      blah( const volatile blah&& b ) {}
      blah( const volatile blah& b ) {}
      blah( const blah& b ) = default;
      blah( blah&& b ) = default;
      blah() = default;
    
      template<class Self>
      friend blah do_foo(Self&& self) {
        std::cout << "Is reference:" << std::is_reference<Self>::value << "\n";
        std::cout << "Is const:" << std::is_const<std::remove_reference_t<Self>>::value << "\n";
        std::cout << "Is volatile:" << std::is_volatile<std::remove_reference_t<Self>>::value << "\n";
        return decltype(self)(self);
      }
    };
    

    test code:

    blah{}.foo();
    blah tmp;
    tmp.foo();
    const blah tmp2;
    tmp2.foo();
    

    Output:

    Is reference:0
    Is const:0
    Is volatile:0
    Is reference:1
    Is const:0
    Is volatile:0
    Is reference:1
    Is const:1
    Is volatile:0
    

    Live example.

    Use the macro to implement the methods that forward to the friend, and write the friend manually. The methods that forward to the friend could even be template<class...Args> if you are ok with the disadvantages of perfect forwarding.

    Having less of your code that runs in a macro will be worth it.

    #define RETURNS(...) \
      noexcept(noexcept(__VA_ARGS__)) \
      -> decltype( __VA_ARGS__ ) \
      { return __VA_ARGS__; }
    
    #define FORWARD_METHOD_TO_FRIEND( NAME, SELF, REF_QUAL, HELPER_NAME ) \
      template<class...Args>
      auto NAME( Args&&... args ) REF_QUAL \
      RETURNS( HELPER_NAME( SELF, std::forward<Args>(args)... ) )
    
    #define FORWARD_CV_METHOD_TO_FRIEND( NAME, SELF, REF_QUAL, HELPER_NAME ) \
      FORWARD_METHOD_TO_FRIEND( NAME, SELF, REF_QUAL, HELPER_NAME ) \
      FORWARD_METHOD_TO_FRIEND( NAME, SELF, const REF_QUAL, HELPER_NAME ) \
      FORWARD_METHOD_TO_FRIEND( NAME, SELF, const volatile REF_QUAL, HELPER_NAME ) \
      FORWARD_METHOD_TO_FRIEND( NAME, SELF, volatile REF_QUAL, HELPER_NAME )
    
    #define FORWARD_SELF_TO_FRIEND( NAME, HELPER_NAME ) \
      FORWARD_CV_METHOD_TO_FRIEND( NAME, *this, &, HELPER_NAME ) \
      FORWARD_CV_METHOD_TO_FRIEND( NAME, std::move(*this), &&, HELPER_NAME ) 
    

    which shortens blah above to:

    struct blah {
      FORWARD_SELF_TO_FRIEND( foo, do_foo )
      // ...
      template<class Self>
      void do_foo( Self&& self )
      {
        // some_blah.foo() invokes do_foo passing it
        // self with the proper qualifications
      }
    };
    

    replacing (and improving) all of the foo() methods.

    some_blah.foo() now invokes do_foo( some_blah ) with proper CV and L/R value qualifications and noexcept on some_blah.

    constexpr may require some love to get right, because we don't have a conditional constexpr test in C++ right now. Maybe simply claiming constexpr in the template foo generated is the right thing, but I'm hazy on the subject.