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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With