In The this pointer [class.this], the C++ standard states:
The type of this in a member function of
a class X is X*.
i.e. this is not const. But why is it then that
struct M {
M() { this = new M; }
};
gives
error: invalid lvalue in assignment <-- gcc
'=' : left operand must be l-value <-- VC++
'=' : left operand must be l-value <-- clang++
'=' : left operand must be l-value <-- ICC
(source: some online compiler frontends)
In other words, this is not const, but it really is!
Because in the same paragraph, it is also mentioned that this is a prvalue ("pure rvalue").
Examples mentioned in the standard for pure rvalue are the result of calling a function which does not return a reference, or literals like 1, true or 3.5f. The this-pointer is not a variable, it's more like a literal that expands to the address of the object for which the function is called ([class.this]). And like e.g. literal true has type bool and not bool const, this is of type X* and not X*const.
Related
I'm trying to understand the consistency in the error that is thrown in this program:
#include <iostream>
class A{
public:
void test();
int x = 10;
};
void A::test(){
std::cout << x << std::endl; //(1)
std::cout << A::x << std::endl; //(2)
int* p = &x;
//int* q = &A::x; //error: cannot convert 'int A::*' to 'int*' in initialization| //(3)
}
int main(){
const int A::* a = &A::x; //(4)
A b;
b.test();
}
The output is 10 10. I labelled 4 points of the program, but (3) is my biggest concern:
x is fetched normally from inside a member function.
x of the object is fetched using the scope operator and an lvalue to the object x is returned.
Given A::x returned an int lvalue in (2), why then does &A::x return not int* but instead returns int A::*? The scope operator even takes precedence before the & operator so A::x should be run first, returning an int lvalue, before the address is taken. i.e. this should be the same as &(A::x) surely? (Adding parentheses does actually work by the way).
A little different here of course, the scope operator referring to a class member but with no object to which is refers.
So why exactly does A::x not return the address of the object x but instead returns the address of the member, ignoring precedence of :: before &?
Basically, it's just that the syntax &A::x (without variation) has been chosen to mean pointer-to-member.
If you write, for example, &(A::x), you will get the plain pointer you expect.
More information on pointers-to-members, including a note about this very property, can be found here.
The C++ standard doesn't explicitly specify operator precedence; it's possible to deduce operator precedence implicitly from the grammar rules, but this approach fails to appreciate the occasional special case like this which doesn't fit into a traditional model of operator precedence.
[expr.unary.op]/3:
The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id. If the operand is a
qualified-id naming a non-static or variant member m of some class C with type T, the result has type 'pointer to member of class C of type
T' and is a prvalue designating C::m. Otherwise, if the type of the expression is T, the result has type 'pointer to T' and is a prvalue that is the address of the designated object or a pointer to the designated function.
/4:
A pointer to member is only formed when an explicit & is used and its operand is a qualified-id not enclosed in parentheses. [ Note:
that is, the expression &(qualified-id), where the qualified-id is enclosed in parentheses, does not form an expression of type 'pointer to member'.
[expr.prim.general]/9:
A nested-name-specifier that denotes a class, optionally followed by the keyword template, and then followed by the name of a member of either that class or one of its base classes, is a qualified-id.
What it all adds up to is that an expression of the form &A::x has the type "pointer to member x of class A" if x is a non-static member of a non-union class A, and operator precedence has no influence on this.
I was trying to post this code as an answer to this question, by making this pointer wrapper (replacing raw pointer). The idea is to delegate const to its pointee, so that the filter function can't modify the values.
#include <iostream>
#include <vector>
template <typename T>
class my_pointer
{
T *ptr_;
public:
my_pointer(T *ptr = nullptr) : ptr_(ptr) {}
operator T* &() { return ptr_; }
operator T const*() const { return ptr_; }
};
std::vector<my_pointer<int>> filter(std::vector<my_pointer<int>> const& vec)
{
//*vec.front() = 5; // this is supposed to be an error by requirement
return {};
}
int main()
{
std::vector<my_pointer<int>> vec = {new int(0)};
filter(vec);
delete vec.front(); // ambiguity with g++ and clang++
}
Visual C++ 12 and 14 compile this without an error, but GCC and Clang on Coliru claim that there's an ambiguity. I was expecting them to choose non-const std::vector::front overload and then my_pointer::operator T* &, but no. Why's that?
[expr.delete]/1:
The operand shall be of pointer to object type or of class type. If of
class type, the operand is contextually implicitly converted (Clause
[conv]) to a pointer to object type.
[conv]/5, emphasis mine:
Certain language constructs require conversion to a value having one
of a specified set of types appropriate to the construct. An
expression e of class type E appearing in such a context is said
to be contextually implicitly converted to a specified type T and
is well-formed if and only if e can be implicitly converted to a type
T that is determined as follows: E is searched for non-explicit
conversion functions whose return type is cv T or reference to cv T
such that T is allowed by the context. There shall be exactly
one such T.
In your code, there are two such Ts (int * and const int *). It is therefore ill-formed, before you even get to overload resolution.
Note that there's a change in this area between C++11 and C++14. C++11 [expr.delete]/1-2 says
The operand shall have a pointer to object type, or a class type
having a single non-explicit conversion function (12.3.2) to a pointer
to object type. [...]
If the operand has a class type, the operand is converted to a pointer type by calling the above-mentioned conversion function, [...]
Which would, if read literally, permit your code and always call operator const int*() const, because int* & is a reference type, not a pointer to object type. In practice, implementations consider conversion functions to "reference to pointer to object" like operator int*&() as well, and then reject the code because it has more than one qualifying non-explicit conversion function.
The delete expression takes a cast expression as argument, which can be const or not.
vec.front() is not const, but it must first be converted to a pointer for delete. So both candidates const int* and int* are possible candidates; the compiler cannot choose which one you want.
The eaiest to do is to use a cast to resolve the choice. For example:
delete (int*)vec.front();
Remark: it works when you use a get() function instead of a conversion, because the rules are different. The choice of the overloaded function is based on the type of the parameters and the object and not on the return type. Here the non const is the best viable function as vec.front()is not const.
As known, the function call which return type is an rvlaue to a function is an lvalue.
A function call is an lvalue if the result type is an lvalue reference
type or an rvalue reference to function type, an xvalue if the result
type is an rvalue reference to object type, and a prvalue otherwise.
#include <iostream>
int a(){ return 1; }
int foo(){ return 1; }
int (&&bar())(){ return a; }
int main()
{
bar() = foo; //error: cannot convert 'int()' to 'int()' in assignment
}
What's wrong with that diagnostic message?
Emphasis mine, [expr.ass]/1:
The assignment operator (=) and the compound assignment operators all group right-to-left. All require a
modifiable lvalue as their left operand and return an lvalue referring to the left operand...
[basic.lval]/6:
Functions cannot be modified, but pointers to functions can be modifiable.
So you may have an lvalue referring to a function but it is not a modifiable lvalue, and cannot be used to modify the function.
The diagnostic message... leaves something to be desired. Clang 3.6 says,
error: non-object type 'int ()' is not assignable
which is clearer.
Came across a proposal called "rvalue reference for *this" in clang's C++11 status page.
I've read quite a bit about rvalue references and understood them, but I don't think I know about this. I also couldn't find much resources on the web using the terms.
There's a link to the proposal paper on the page: N2439 (Extending move semantics to *this), but I'm also not getting much examples from there.
What is this feature about?
First, "ref-qualifiers for *this" is a just a "marketing statement". The type of *this never changes, see the bottom of this post. It's way easier to understand it with this wording though.
Next, the following code chooses the function to be called based on the ref-qualifier of the "implicit object parameter" of the function†:
// t.cpp
#include <iostream>
struct test{
void f() &{ std::cout << "lvalue object\n"; }
void f() &&{ std::cout << "rvalue object\n"; }
};
int main(){
test t;
t.f(); // lvalue
test().f(); // rvalue
}
Output:
$ clang++ -std=c++0x -stdlib=libc++ -Wall -pedantic t.cpp
$ ./a.out
lvalue object
rvalue object
The whole thing is done to allow you to take advantage of the fact when the object the function is called on is an rvalue (unnamed temporary, for example). Take the following code as a further example:
struct test2{
std::unique_ptr<int[]> heavy_resource;
test2()
: heavy_resource(new int[500]) {}
operator std::unique_ptr<int[]>() const&{
// lvalue object, deep copy
std::unique_ptr<int[]> p(new int[500]);
for(int i=0; i < 500; ++i)
p[i] = heavy_resource[i];
return p;
}
operator std::unique_ptr<int[]>() &&{
// rvalue object
// we are garbage anyways, just move resource
return std::move(heavy_resource);
}
};
This may be a bit contrived, but you should get the idea.
Note that you can combine the cv-qualifiers (const and volatile) and ref-qualifiers (& and &&).
Note: Many standard quotes and overload resolution explanation after here!
† To understand how this works, and why #Nicol Bolas' answer is at least partly wrong, we have to dig in the C++ standard for a bit (the part explaining why #Nicol's answer is wrong is at the bottom, if you're only interested in that).
Which function is going to be called is determined by a process called overload resolution. This process is fairly complicated, so we'll only touch the bit that is important to us.
First, it's important to see how overload resolution for member functions works:
§13.3.1 [over.match.funcs]
p2 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. [...]
p3 Similarly, when appropriate, the context can construct an argument list that contains an implied object argument to denote the object to be operated on.
Why do we even need to compare member and non-member functions? Operator overloading, that's why. Consider this:
struct foo{
foo& operator<<(void*); // implementation unimportant
};
foo& operator<<(foo&, char const*); // implementation unimportant
You'd certainly want the following to call the free function, don't you?
char const* s = "free foo!\n";
foo f;
f << s;
That's why member and non-member functions are included in the so-called overload-set. To make the resolution less complicated, the bold part of the standard quote exists. Additionally, this is the important bit for us (same clause):
p4 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. [...]
p5 During overload resolution [...] [t]he implicit object parameter [...] retains its identity since conversions on the corresponding argument shall obey these additional rules:
no temporary object can be introduced to hold the argument for the implicit object parameter; and
no user-defined conversions can be applied to achieve a type match with it
[...]
(The last bit just means that you can't cheat overload resolution based on implicit conversions of the object a member function (or operator) is called on.)
Let's take the first example at the top of this post. After the aforementioned transformation, the overload-set looks something like this:
void f1(test&); // will only match lvalues, linked to 'void test::f() &'
void f2(test&&); // will only match rvalues, linked to 'void test::f() &&'
Then the argument list, containing an implied object argument, is matched against the parameter-list of every function contained in the overload-set. In our case, the argument list will only contain that object argument. Let's see how that looks like:
// first call to 'f' in 'main'
test t;
f1(t); // 't' (lvalue) can match 'test&' (lvalue reference)
// kept in overload-set
f2(t); // 't' not an rvalue, can't match 'test&&' (rvalue reference)
// taken out of overload-set
If, after all overloads in the set are tested, only one remains, the overload resolution succeeded and the function linked to that transformed overload is called. The same goes for the second call to 'f':
// second call to 'f' in 'main'
f1(test()); // 'test()' not an lvalue, can't match 'test&' (lvalue reference)
// taken out of overload-set
f2(test()); // 'test()' (rvalue) can match 'test&&' (rvalue reference)
// kept in overload-set
Note however that, had we not provided any ref-qualifier (and as such not overloaded the function), that f1 would match an rvalue (still §13.3.1):
p5 [...] For non-static member functions declared without a ref-qualifier, an additional rule applies:
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.
struct test{
void f() { std::cout << "lvalue or rvalue object\n"; }
};
int main(){
test t;
t.f(); // OK
test().f(); // OK too
}
Now, onto why #Nicol's answer is atleast partly wrong. He says:
Note that this declaration changes the type of *this.
That is wrong, *this is always an lvalue:
§5.3.1 [expr.unary.op] p1
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.
§9.3.2 [class.this] p1
In the body of a non-static (9.3) member function, the keyword this is a prvalue expression whose value is the address of the object for which the function is called. The type of this in a member function of a class X is X*. [...]
There is an additional use case for the lvalue ref-qualifier form. C++98 has language that allows non-const member functions to be called for class instances that are rvalues. This leads to all kinds of weirdness that is against the very concept of rvalueness and deviates from how built-in types work:
struct S {
S& operator ++();
S* operator &();
};
S() = S(); // rvalue as a left-hand-side of assignment!
S& foo = ++S(); // oops, dangling reference
&S(); // taking address of rvalue...
Lvalue ref-qualifiers solve these problems:
struct S {
S& operator ++() &;
S* operator &() &;
const S& operator =(const S&) &;
};
Now the operators work like those of the builtin types, accepting only lvalues.
Let's say you have two functions on a class, both with the same name and signature. But one of them is declared const:
void SomeFunc() const;
void SomeFunc();
If a class instance is not const, overload resolution will preferentially select the non-const version. If the instance is const, the user can only call the const version. And the this pointer is a const pointer, so the instance cannot be changed.
What "r-value reference for this` does is allow you to add another alternative:
void RValueFunc() &&;
This allows you to have a function that can only be called if the user calls it through a proper r-value. So if this is in the type Object:
Object foo;
foo.RValueFunc(); //error: no `RValueFunc` version exists that takes `this` as l-value.
Object().RValueFunc(); //calls the non-const, && version.
This way, you can specialize behavior based on whether the object is being accessed via an r-value or not.
Note that you are not allowed to overload between the r-value reference versions and the non-reference versions. That is, if you have a member function name, all of its versions either use the l/r-value qualifiers on this, or none of them do. You can't do this:
void SomeFunc();
void SomeFunc() &&;
You must do this:
void SomeFunc() &;
void SomeFunc() &&;
Note that this declaration changes the type of *this. This means that the && versions all access members as r-value references. So it becomes possible to easily move from within the object. The example given in the first version of the proposal is (note: the following may not be correct with the final version of C++11; it's straight from the initial "r-value from this" proposal):
class X {
std::vector<char> data_;
public:
// ...
std::vector<char> const & data() const & { return data_; }
std::vector<char> && data() && { return data_; }
};
X f();
// ...
X x;
std::vector<char> a = x.data(); // copy
std::vector<char> b = f().data(); // move
I saw this kind of cast for the first time today, and I'm curious as to why this works. I thought casting in this manner would assign to the temporary, and not the class member. Using VC2010.
class A
{
public:
A() :
m_value(1.f)
{
((float)m_value) = 10.f;
}
const float m_value;
};
Even after fixing all other problems to make the code compile, it only works in VC2010 because it uses a non-standard extension. And If you specify /Wall to see all warnings, you compiler will emit
warning C4213: nonstandard extension used : cast on l-value
It shouldn't work. An explicit type conversion to float with cast notation will be a prvalue (§5.4):
The result of the expression (T) cast-expression is of type T. The result is an lvalue if T is an lvalue reference type or an rvalue reference to function type and an xvalue if T is an rvalue reference to object type; otherwise the result is a prvalue.
My emphasis added.
The assignment operator requires an lvalue as its left operand (§5.17):
All require a modifiable lvalue as their left operand and return an lvalue referring to the left operand.
A prvalue is not an lvalue.