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.
Related
This question already has answers here:
Why can a member function be called on a temporary but a global function cannot?
(2 answers)
Closed 6 months ago.
I have the following code:
struct S {
operator int(); // F1
operator double(); // F2
};
int main() {
int res = S();
}
Since neither F1 nor F2 is cv-qualified, the type of the implicit object parameter is S&, and the corresponding argument to be matched against is S().
Now, per [over.match.viable]/4: (emphasis mine)
Third, for F to be a viable function, there shall exist for each
argument an implicit conversion sequence that converts that argument
to the corresponding parameter of F. If the parameter has reference
type, the implicit conversion sequence includes the operation of
binding the reference, and the fact that an lvalue reference to
non-const cannot bind to an rvalue and that an rvalue reference cannot
bind to an lvalue can affect the viability of the function (see
[over.ics.ref]).
According to the above quote (bold), I'm expecting that neither F1 nor F2 is viable, because the implicit object parameter for both is of type S& and it cannot bind to an rvalue, S().
But, when I tried to compile the code, I found out both functions are viable candidates. Which is best match is not what I am asking about here.
So, why are both F1 and F2 viable candidates, even though the implicit object parameter (of type S&) cannot bind to class prvalue S()?
The main concern expressed in your question appears to be how a given rvalue argument can bind to an implicitly declared lvalue reference parameter. (I'm not here even attempting to make an adjudication on the extensive discusssion in the comments to your question about whether or not any actual overloads are involved in your code sample.)
This (main) concern is addressed – quite clearly, IMHO – in another part of the [over.match.funcs] section of the C++ Standard you cite (bold emphasis mine):
12.2.2.1 General[over.match.funcs.general]
…
5 During overload resolution, the implied
object argument is indistinguishable from other arguments. The implicit
object parameter, however, retains its identity since no user-defined
conversions can be applied to achieve a type match with it. For
implicit object member functions declared without a ref-qualifier,
even if the implicit object parameter is not const-qualified, an
rvalue can be bound to the parameter as long as in all other respects
the argument can be converted to the type of the implicit object
parameter.
Without this paragraph, implicit conversion functions would lose a great deal of their usefulness, such as the use-case you have provided in your example.
In this link : Implicit object parameter
In this quote :
If any candidate function is a member function (static or non-static) that does not have an explicit object parameter (since C++23), but not a constructor, it is treated as if it has an extra parameter (implicit object parameter) which represents the object for which they are called and appears before the first of the actual parameters.
I do not understand why the word static is mentioned here? Isn't the implicit object parameter the this pointer ( which only exists in non-static functions ) ?
Edit
in this link : link
quote :
The keyword this is a rvalue (until C++11)prvalue (since C++11) expression whose value is the address of the implicit object parameter (object on which the non-static member function is being called). It can appear in the following contexts:
It's useful to consider examples. When you have:
struct C {
void f(int);
void f(int) const;
};
C c;
c.f(42);
How does overload resolution pick? You effectively have a choice of:
// implicit object | regular
// parameter | parameter
void f(C&, int );
void f(C const&, int );
With the arguments (C, int). That ends up picking the first one, for being a better match.
Now, let's think of this example:
struct D {
static void g(int);
void g(long);
};
D d;
d.g(42);
Now, if we try to do the same thing:
// implicit object | regular
// parameter | parameter
void g(????????, int );
void g(D&, long );
We have two arguments, a D and an int. We don't know if we're going to call a static function or not yet, we still have to do overload resolution. How do we pick in this case? The non-static member function has an implicit object parameter, D&, but what do we do for the static one?
The C++ answer is we contrive a fake parameter, that is a perfect match for everything:
// implicit object | regular
// parameter | parameter
void g(contrived-match, int );
void g(D&, long );
And now, when we do overload resolution with (D, int), you can see that the static function is the best match (better conversion sequence for the second parameter).
Once we pick the static member function, we then ignore the object argument entirely. d.f(42) basically evaluates as D::f(42). But we didn't know that until we performed overload resolution - the contrived parameter exists to solve the problem of how to actually compare these cases.
This still applies even if there were just the one static member function - since d.f(42) does have two parameters: the d and the 42, so the language needs to handle the d somehow (the alternative could've been to simply disallow this syntax, requiring D::f(42) if you wanted to call a static member function, but that seems a lot less nice).
Consider what happens if you don't have this rule and have a static method and non-static method with the same (explicit) parameters. Then to the non-static method an additional implicit parameter (this) will be added, but not to the static method. This will make the list of parameters of both methods different and will allow to overload the static method with non-static method with the same explicit parameters.
First things first, there is a difference between implicit object parameter and this pointer. The former is a reference type while the latter is a keyword and is an rvalue of pointer type. For example for a const qualified non-static member function the implicit object parameter is of type const X& while the this pointer is of type const X*. While for a non-const nonstatic member function the implicit object parameter is of type X& and the this is of type X*. This can be confirmed here.
isn't Implicit object parameter the ( this ) pointer ( which ( the ( this ) pointer ) only works with non-static functions )
No, both static as well as non static member functions have an implicit object parameter for the purposes of overload resolution as can be seen from over.match.funcs#2 which states:
The set of candidate functions can contain both member and non-member functions to be resolved against the same argument list. So that argument and parameter lists are comparable within this heterogeneous set, a member function is considered to have an extra parameter, called the implicit object parameter, which represents the object for which the member function has been called. For the purposes of overload resolution, both static and non-static member functions have an implicit object parameter, but constructors do not.
(emphasis mine)
cppreference.com about std::add_lvalue_reference/std::add_rvalue_reference:
If T is an object type or a function type that has no cv- or ref- qualifier, provides a member typedef type which is T&&, otherwise type is T.
Does it mean that if T is const or volatile than T is not converted to reference? If no, then what does it mean "has no cv-qualifier".
Does it mean that if T is const or volatile than T is not converted to reference?
Yes, but only if it is a function.
The quote from cppreference can be a little confusing. cppreference has
If T is an object type or a function type that has no cv- or ref- qualifier, provides a member typedef type which is T&&, otherwise type is T.
which can make you believe the cv-qualifer part applies to both objects and functions, but this is not the case. The actual text from the standard is
an object type, a function type that does not have cv-qualifiers or a ref-qualifier, or a reference type
so any object type and any function without a cv-qualifier and ref-qualifier will yield a reference. references or functions with a cv-qualifier and/or ref-qualifier will yield T
When you see cv-qualifier that means const and or volatile qualifier, i.e. it is a const and or volatile object or const or volatile qualified function. ref-qualifier that means reference qualifier, i.e. the function can only be called on an lvalue or rvalue: void foo() & or void foo() &&.
It's trying to say that the result is respectively T& or T&& if either:
T is an object type, or
T is a function type that does not have any cv-qualifiers and does not have a ref-qualifier.
The restriction "has no cv- or ref-qualifier" does not apply to object types. It applies to function types because if a function type has a cv- or ref-qualifier, then it is not possible to create the referenced type. See Abominable Function Types for some discussion.
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.
I wrote this code in C++:
class Foo
{
public:
int& fun(){return var;} // 1st fun
int fun() const {return var;} // 2rd fun
private:
int var;
};
int main()
{
Foo foo;
int i = foo.fun();
return 0;
}
I know that C++ cannot discriminate overloading function by return value,but why when I added a const to 2rd function ,overloading can work ?
What the 'const' have done ?
Compiler cannot discriminate by return type because return values can undergo conversion before the assignment is performed. The object on which the function is invoked, on the other hand, is a parameter (albeit an implicit one) to the function, so the compiler can discriminate on it.
Const is used in the following way:
Foo inst1;
const Foo inst2;
inst1.fun(); // 1st fun
inst2.fun(); // 2nd fun
Const after the name of the function refers to the implicit this parameter. So, for inst1 it will Foo* and for inst2 const Foo*. This will guide the overload.
The return value is not used for selecting the overload. Methods/functions with the same set of params and different types of return value are not allowed on the same layer.
For what it's worth, the language from the standard (§13.3.1/3,4):
Similarly, when appropriate, the context can construct an argument list that contains an implied object
argument to denote the object to be operated on. Since arguments and parameters are associated by position within their respective lists, the convention is that the implicit object parameter, if present, is
always the first parameter and the implied object argument, if present, is always the first argument.
For non-static member functions, the type of the implicit object parameter is
— “lvalue reference to cv X” for functions declared without a ref-qualifier or with the & ref-qualifier
— “rvalue reference to cv X” for functions declared with the && ref-qualifier
where X is the class of which the function is a member and cv is the cv-qualification on the member function declaration. [ Example: for a const member function of class X, the extra parameter is assumed to have type “reference to const X”. —end example ]