c++c++11language-design

Why is it impossible to cast ref-unqualified member function pointer to a ref-qualified member function pointer?


This code is rejected by all compilers I could test it on, so I suppose it is ill-formed according to the standard.

struct X {
    void f();
};

void (X::*fptr)() & = &X::f; // ERROR

Here we try to cast a ref-unqualified member function pointer (i.e. a pointer that can be invoked with either lvalue or rvalue reference) to a ref-qualified member function pointer (i.e. a pointer that can be invoked only with an lvalue reference). In other words, here we try to cast a type with looser restrictions to a type with stronger restrictions.

In every other part of the language adding restrictions to types is usually legal (e.g. non-const to const conversion), but here it is not.

It seems to me that this limitation was introduced by the committee intentionally, because a somewhat similar example with loosing noexcept qualifications (which were also introduced in C++11) compiles fine:

struct X {
    void f() noexcept;
};

void (X::*fptr)() = &X::f; // OK

Was this limitation intentional? If it was, what were the reasons to impose them?


Solution

  • It's true that C++ doesn't let you convert from void (X::*)() to void (X::*)() & but it's also true that C++ doesn't let you convert from void (X::*)() const to void (X::*)() or from void (*)(const X*) to void (*)(X*).

    But you know what you can do? You can implicitly convert from void (B::*)() to void (D::*)() (where D is derived from B) and you can do the inverse conversion using static_cast.

    This seems a bit strange, right? It seems like converting from void (B::*)() to void (D::*)() ought to be a "bigger" change than converting from void (X::*)() const to void (X::*)(), yet the former is allowed by the language and the latter isn't.

    (You can perform any of these disallowed conversions using reinterpret_cast, but the standard then provides no guarantees as to what happens if you try to call the resulting function pointer. The behaviour is implementation-defined. Your implementation might define it to do what you want it to do, in which case, good for you, you can do it.)

    The reason for all of this is: if no one thought that a particular kind of conversion was useful enough that they wanted to do the hard work of proposing to allow it, then it never became allowed.

    If you want to propose it, you're going to have to provide strong motivation. How often would you actually benefit from being able to do this? What are the workarounds for not being able to do it? How much better would it be to have this feature than to have to use the workarounds? And you're also going to have to do research into whether all C++ implementations can actually do this. Are you sure that there is not any C++ implementation on which void (X::*)() & has a different calling convention from void (X::*)()?