11

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?

Tomilov Anatoliy
  • 15,657
  • 10
  • 64
  • 169
  • 1
    The part about programmatically finding out whether the member is qualified e.g. `&` or `&&` probably deserves its own, separate question. E.g. it’s not really related to forward refs (quite the opposite), which is what you’re using to draw the comparison for what I feel is the main question. – Luc Danton Sep 30 '15 at 12:27
  • @LucDanton In above context the problems is strongly interconnected. – Tomilov Anatoliy Oct 01 '15 at 03:22
  • 1
    I don’t see how. Here’s an attempt at a summary: 'how come forwarding references allow abstraction over cv-ref qualifiers whereas that is not possible for the implicit parameter?' on the one hand, 'can or will code find out the cv-ref qualifiers that applied to the argument to the implicit parameter?' on the other. – Luc Danton Oct 01 '15 at 03:27
  • @LucDanton If one have single "forwarding" *cv-ref-qualified* function, then code it composes should know which overloading chosen during every particular instantiation, isn't it? – Tomilov Anatoliy Oct 01 '15 at 03:32
  • Are you now asking a third question? – Luc Danton Oct 01 '15 at 03:35
  • @LucDanton In my view - is not. But my system of knowledge may differ from your. And my conception about the problem, respectively, too. – Tomilov Anatoliy Oct 01 '15 at 03:41

1 Answers1

1

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 :-)

Rumburak
  • 3,416
  • 16
  • 27
  • The benefit of the member function over the global function is not only the syntax, especially when the class is template and you are dealing with overloads. In the global scope you have to somehow differentiate between what in the class scope would be `A::f(T)` and `A::f(U)`. – Vahagn Oct 24 '16 at 18:42