Title
Is the & ref-qualifier needed?
Status
nad
Section
9.3 [dcl.decl]
Submitter
Alberto Ganesh Barbati

Created on 2008-10-16.00:00:00 last changed 182 months ago

Messages

Date: 2009-03-23.00:00:00

Rationale, March, 2009:

The CWG did not feel that the suggested change was a signficant improvement over the existing specification.

Date: 2022-11-20.07:54:16

Do we really need the & ref-qualifier? We could get the same behavior without it if we relaxed the restriction on ref-qualified and non-ref-qualified overloads in the same set:

    with the & ref-qualifier without the & ref-qualifier
        struct S {
          void f();
        };
    
        struct S {
          void f();
        };
    
        struct S {
          void f() &;
        };
    
    
        struct S {
          void f();
          void f() && = delete;
        };
    
        struct S {
          void f() &&;
        };
    
        struct S {
          void f() &&;
        };
    
        struct S {
          void f() &;
          void f() &&;
        };
    
        struct S {
          void f();
          void f() &&;
        };
    

The main objection I can see to this change is that we would lose the notational convenience of the & ref-qualifier, which would need to be replaced by a pair of declarations. We might overcome this by still allowing a single & on a function (although it would not be a ref-qualifier) as a synonym to a non-ref-qualified declaration plus a deleted ref-qualified declaration.

The biggest asymmetry between the implicit object parameter and regular parameters is not in reference binding but in type deduction. Consider:

    template <class R, class C, class A> void f(R (C::*p)(A));

With these members:

    struct S {
       void mv(std::string);
       void mr(std::string&);
       void ml(std::string&&);
    };

then

    f(&S::mv); // deduces A = string
    f(&S::mr); // deduces A = string&
    f(&S::ml); // deduces A = string&&

On the other hand, with these members:

    struct S {
       void mv(std::string);
       void mr(std::string) &;
       void ml(std::string) &&
    };

then

  f(&S::mv); // deduces C = S
  f(&S::mr); // illegal
  f(&S::ml); // illegal

To make template f work with any pointer to member function, I need three overloads of f. Add cv-qualifiers and it's twelve overloads!

And then there is the interaction with concepts. Consider this type:

    struct Value {
        Value& operator=(const Value&) &;
    };

Is it, say, Regular? If so, will the following compile, and what is the outcome?

    template <Regular T> void f() {
      T() = T();
    }

    void g() {
      f<Value>();
    }

If Value is not Regular, that is a good motivation to avoid ever using & ref-qualifiers on operator= (and probably on any member functions).

If Value is Regular, then either f<Value>() doesn't compile, violating one of the principal motivations for concepts, or it calls Value::operator= on an rvalue, which was explicitly prohibited.

History
Date User Action Args
2009-03-23 00:00:00adminsetmessages: + msg2033
2009-03-23 00:00:00adminsetstatus: open -> nad
2008-10-16 00:00:00admincreate