std::add_pointer implementation for non-static member functions - c++

This question is a follow-up of A question regarding the implementation of std::add_pointer
Under std::add_pointer
there is the following reference:
Otherwise (if T is a cv- or ref-qualified function type), provides the
member typedef type which is the type T.
Based on reading Non-static member functions: const-, volatile-, and ref-qualified member functions, my understanding is that a for a non-static member function with given cvand/or ref qualification,
a) the cv qualification of the function applies to the this pointer as well, within the scope of the function
b) the ref qualification of the function does not apply to the this pointer within the scope of the function
Given this, why is it that std::add_pointer cannot provide the member typedef type T* in the case of a non-static member function with cv or ref qualification?

Per [dcl.ptr]/4:
[ Note: Forming a pointer to reference type is ill-formed; see
[dcl.ref]. Forming a function pointer type is ill-formed if the
function type has cv-qualifiers or a ref-qualifier; see
[dcl.fct]. Since the address of a bit-field cannot be taken, a
pointer can never point to a bit-field.  — end
note ]
The pointer-to-cv-qualified-function type you are imagining is actually nonexistent. Therefore, std::add_pointer cannot produce such a type :)

A non-static member function type cannot be formed. Such a thing does not exist.
struct T {
int func() const;
};
func does not have a type. You cannot ever use it as an expression on its own.
using mf = int (T::*)() const;
mf myfunc = &T::func;
mf is a pointer to member function type. It is not a function type; pointers to non-static members (including member functions) are data, not functions.
A cv or ref qualification on a member function does not qalify the function type, which does not exist, but the type of the implicit "this" parameter.
The paragraph you quote only applies to non-member or static member functions.

Related

C++ syntax: & in front of class name in Pybind11 [duplicate]

We know that to create a "common" pointer to a function we can do for example:
void fun();
void (*ptr)() = fun;
The name of a function is also the address where the function start. So I do not need to use the address operator & like this:
void (*ptr)() = &fun;
Now for a pointer to a member function on the contrary I must use the address operator. For example for a class A with a pointer to member function ptr and a function fun() I must write:
void(A::*ptr)() = &A::fun;
Why this difference?
According to the C++ standard:
4.3 Function-to-pointer conversion [conv.func]
An lvalue of function type T can be converted to a prvalue of type
“pointer to T”. The result is a pointer to the function.
This conversion never applies to non-static member functions because
an lvalue that refers to a non-static member function cannot be
obtained.
I think difference is because A::fun is non-static member of class A. I mean, if your fun() is static member of A it will be like for ordinary function. Try it.
It is because now that function is defined inside the class. Pointer to member function holds the "relative address" of where the function is in the class layout and so you have to access it that way.
In case of static, it has no this pointer and it behaves like a global function and so, you can access it like normal function pointer.

Contradicting definition of implicit this parameter in the standard

I am learning about classes in C++. I came across the following statement from the standard:
During overload resolution, non-static cv-qualified member function of class X is treated as a function that takes an implicit parameter of type lvalue reference to cv-qualified X if it has no ref-qualifiers or if it has the lvalue ref-qualifier. Otherwise (if it has rvalue ref-qualifier), it is treated as a function taking an implicit parameter of type rvalue reference to cv-qualified X.
The above statement seems to imply that for a const qualified non-static member function of a class X will have an implicit parameter of type const X&.
But then i also came across:
The type of this in a member function of class X is X* (pointer to X). If the member function is cv-qualified, the type of this is cv X* (pointer to identically cv-qualified X). Since constructors and destructors cannot be cv-qualified, the type of this in them is always X*, even when constructing or destroying a const object.
So according to the above second quote the implicit this parameter for a const qualified non-static member function of class Xhas type const X*.
My question is that why is there such a difference. I mean during overload resolution for a const qualfied nonstatic member function, why is the implicit parameter considered as a const X& and not simply a const X* which seems to be the actual type of this.
The implicit object parameter is not the same as this. this is a pointer referring to the object on which the member function was called while the implicit object parameter is the imagined first parameter of the member function, which is passed the object expression in the member function call (whats left of . in the member access expression) and so should be a reference parameter.
It wouldn't make sense to use a pointer for the implicit object parameter. It would make it impossible to overload the function on value category of the object expression. If a member function is &&-qualified, the implicit object parameter is a rvalue reference, so that if the object expression is a rvalue overload resolution correctly overload with a &-qualified member function.
So the implicit object parameter is const T& in your example, but this has type const T*. There is no contradiction.

Is decltype of a non-static member function ill-formed?

I'm not sure to perfectly understand [dcl.type]/4.3:
For an expression e, the type denoted by decltype(e) is defined as follows:
[...]
(4.3) otherwise, if e is an unparenthesized id-expression or an unparenthesized class member access, decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;
[...]
For me, the emphasized part both apply to id-expression and class member access, right?
Playing with my favorite compiler, I get the following.
✓ Accepted by the compiler
namespace N { void f() {} }
using type = decltype(N::f);
type* pf = N::f;
Ok I guess; N::f is an unparenthesized id-expression and does not name a set of overloaded functions.
✗ Rejected by the compiler
namespace N { void f() {} void f(int) {} }
using type = decltype(N::f); // error: decltype cannot resolve address of overloaded function
type* pf = N::f;
Ok; N::f does name a set of overloaded functions.
✗ Rejected by the compiler
struct S { void f(){} };
using type = decltype(S::f); // error: invalid use of non-static member function 'void S::f()'
type* pf = &S::f;
Hum? S::f would name a set of one overloaded function?
All in all, is my understanding of [dcl.type]/4.3 just bad? is gcc trunk wrong? both? none? kamoulox?
The simple reason is that the use of S::f is constrained for class members.
[expr.prim.id]
2 An id-expression that denotes a non-static data member or
non-static member function of a class can only be used:
as part of a class member access in which the object expression refers to the member's class or a class derived from that class, or
to form a pointer to member ([expr.unary.op]), or
if that id-expression denotes a non-static data member and it appears in an unevaluated operand.
The last bullet, the one related to your code, only applies to non-static data member. There is no provision for functions.
I can only speculate as to why it's not allowed, though I previously asked that question.
It's worth a note that decltype(&S::f) works here as pointer-to-member-function type,
again unless the f names a set of overloaded (member) functions.
The function type itself can be extracted from the pointer-to-member-function type.
If the member function is cv-or-ref qualified then it has an abominable function type.
The std traits are lacking here - a library like Boost.CallableTraits helps.

this in unevaluated context in static member functions

Why this is not allowed in unevaluated context in static member functions?
struct A
{
void f() {}
static void callback(void * self) // passed to C function
{
static_cast< decltype(this) >(self)->f();
}
};
This code gives an error:
error: 'this' is unavailable for static member functions
static_cast< decltype(this) >(self)->f();
^~~~
decltype(this) there is needed for brevity (sometimes it is much more shorter, then VeryVeryLongClassName *), another advantage is the fact that intention is more clear.
What Standard says about using this in unevaluated contexts in static member functions?
I don't see how it matters that this appears within an unevaluated context, you've referred to something that doesn't exist in a static member function, so how is the compiler supposed to deduce the type of this within this context?
As a corollary, the type of this in a non-static member function is dependent on the cv-qualifier of said member function, decltype(this) would yield T const* if the member function were const, and T * if it weren't. Thus, the type is dependent on the context of the expression. In your example, the context has no this pointer.
To alleviate the pain of having to name the class you could add an alias for it.
class VeryVeryLongClassName
{
using self = VeryVeryLongClassName;
};
The current draft of the Standard has some conflicting information.
If a declaration declares a member function or member function template of a class X, the expression this is a prvalue of type "pointer to cv-qualifier-seq X" wherever X is the current class between the optional cv-qualifier-seq and the end of the function-definition, member-declarator, or declarator.
It shall not appear within the declaration of either a static member function or an explicit object member function of the current class (although its type and value category are defined within such member functions as they are within an implicit object member function).
First it prohibits the expression this in a static member function, but then it says that the type and value category of this are defined within static member functions (and it does so in a way which resolves the question of cv qualification). Which seems useless if there is no way to refer to said this to obtain its type.
A potential resolution would be for the rule to change from "shall not appear" to "shall not be evaluated", which would immediately permit it to appear in unevaluated context such as decltype and sizeof.

Address operator with pointer to member function

We know that to create a "common" pointer to a function we can do for example:
void fun();
void (*ptr)() = fun;
The name of a function is also the address where the function start. So I do not need to use the address operator & like this:
void (*ptr)() = &fun;
Now for a pointer to a member function on the contrary I must use the address operator. For example for a class A with a pointer to member function ptr and a function fun() I must write:
void(A::*ptr)() = &A::fun;
Why this difference?
According to the C++ standard:
4.3 Function-to-pointer conversion [conv.func]
An lvalue of function type T can be converted to a prvalue of type
“pointer to T”. The result is a pointer to the function.
This conversion never applies to non-static member functions because
an lvalue that refers to a non-static member function cannot be
obtained.
I think difference is because A::fun is non-static member of class A. I mean, if your fun() is static member of A it will be like for ordinary function. Try it.
It is because now that function is defined inside the class. Pointer to member function holds the "relative address" of where the function is in the class layout and so you have to access it that way.
In case of static, it has no this pointer and it behaves like a global function and so, you can access it like normal function pointer.