If there are no another overloadings (say, f(T &)
or f(volatile T &&)
) of a (member) function template template< typename T > f(T &&);
, then T &&
is so-called forwarding reference, and T
is either U
, or U &
for some cv-qualified type U
. But for cv-ref-qualifiers of member functions there is no such a rule. In struct S { void f() && { ; } };
a S::f()
has always rvalue-reference qualifier.
In generic code it would be very useful to avoid a definition of 4 (or even 8, if we also consider volatile
qualifier) overloadings of some member function, in cases if all of them doing generally the same thing.
Another problem that arises in this way, it is impossibility to define an effective cv-ref-qualifier of *this
in a particular sense. Following code not allows one to determine whether the ref-qualifier of a member function operator ()
is &&
of &
.
#include <type_traits>
#include <utility>
#include <iostream>
#include <cstdlib>
#define P \
{ \
using this_ref = decltype((*this)); \
using this_type = std::remove_reference_t< this_ref >; \
std::cout << qual() << ' ' \
<< (std::is_volatile< this_type >{} ? "volatile " : "") \
<< (std::is_const< this_type >{} ? "const " : "") \
<< (std::is_lvalue_reference< this_ref >{} ? "&" : "&&") \
<< std::endl; \
}
struct F
{
constexpr int qual() & { return 0; }
constexpr int qual() const & { return 1; }
constexpr int qual() && { return 2; }
constexpr int qual() const && { return 3; }
constexpr int qual() volatile & { return 4; }
constexpr int qual() volatile const & { return 5; }
constexpr int qual() volatile && { return 6; }
constexpr int qual() volatile const && { return 7; }
void operator () () & P
void operator () () const & P
void operator () () && P
void operator () () const && P
void operator () () volatile & P
void operator () () volatile const & P
void operator () () volatile && P
void operator () () volatile const && P
};
int
main()
{
{
F v;
F const c{};
v();
c();
std::move(v)();
std::move(c)();
}
{
volatile F v;
volatile F const c{};
v();
c();
std::move(v)();
std::move(c)();
}
return EXIT_SUCCESS;
}
But it would be very nice, if there was above syntax. I.e. decltype((*this))
denote exact cv-ref-qualified type of *this
. It would not be a breaking-change to introduce such a syntax into coming version of the C++ standard at my mind. But &&
as forwarding cv-ref-qualifier is (and it looks like an omission of the committee (namely, core language working group)).
Another sequence is possible to denote both the member function cv-ref-qualifier and cv-ref-qualified type of *this
into its body: auto &&
, decltype(&&)
etc.
Is there a proposal regarding this issue, prepared for use in C++17?
Yes, there are such proposals.
Background:
Since we already have forwarding references in template functions, you could simply turn your member function into a template friend function (and protect it via enable_if
from being used with any other class than F
, if required).
Now, maybe you want your really, really want to use your function as a member function, because you really, really like that syntax so much better.
The proposals:
Look up unified call syntax proposals, for instance: n4174
If something like that is accepted, it you will be able to use free functions like member functions of the first argument. This would cover the example code you linked in your first comment. Admittedly, it would not cover operator(), but I consider that a minor nuisance compared to writing 8 overloads :-)
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