Note: To clarify, the question is not about the use of the restrict
keyword in general, but specifically about applying it to member functions
I'm afraid that I don't clearly understand why you're talking about this
.
restrict
specifies that a pointer is not overlapped with others. So, compilers can assume the memory regions pointed by restrict pointers are not dependent, which allows more aggressive optimizations. __restrict__
would be much more effective when used for other pointer variables than this
.
So, what would be a case where one would actually want to give a member a restrict qualifier and where it makes sense?
Just recall a representative case of using restrict
pointers in memcpy
:
void Foo::MyCompute(__restrict__ char* bufA, __restrict__ char* BufB)
{
}
The link you posted is interesting. There will not be a solid use case for having restrict
applied on this
. As you mentioned in your question, copy constructor, operator = could have been potential candidates; but compiler can take care of them.
But following case can be interesting
struct A
{
//...
void Destroy (A*& p) __restrict__
{
delete this;
p = 0;
p++;
}
};
Now use case can be;
A **pp = new A*[10];
for(int i = 0; i < 10; i++)
pp[i] = new A;
//...
A* p = pp[0];
for(int i = 0; i < 10; i++)
p->Destroy(p);
delete[] pp;
Though this is very unusual practice, this is the only case I could think of this use case.
Either I am missing something, or your question does not make sense. this
is not that different from any other argument to a member function, so why are you surprised that GCC allows you to apply restrict
to it?
Regarding applying it to an assignment operator, you rightly point out that it would obviate the need for an explicit self-assignment test. Then you say:
Obviously, this is something that you cannot possibly know in advance
But this is always true when you use restrict
for anything. For example, somebody might decide to call memcpy
with overlapping memory regions; you "cannot possibly know in advance" that they will not do so. But the restrict
declaration for the arguments of memcpy
means they have committed an error if they do. In exactly the same way, if you declare an assignment operator restrict
, you have made it an error for someone to self-assign objects of that class. There is nothing mysterious or contradictory about this at all; it is just part of the semantics of restrict
that it imposes certain constraints on the rest of your code.
I am also not sure why you find it so impossible for a member function to take a pointer (or reference) to another object of the same type. Trivial example:
class Point {
public:
double distance(const Point &other) const;
};
This sort of thing crops up all the time.
So the real question is, why do you think this
is so different from any other argument? Or if you prefer, how did I miss your point so completely?
Adding this as an answer, because it's probably better suited as such (it's kind of an answer and doesn't really belong to the question, plus it's a bit long for a comment).
After thinking about Nemo's answer for a long time, I believe that our both interpretation about self-assignment is maybe somewhat wrong (though Nemo's was more correct than mine). As Nemo correctly pointed out, having a restrict-qualified this
actually means that presence of aliasing is a program error. No more, no less.
Insofar, when writing this, your logic should actually not be "since you say this cannot happen, you consequentially should not check for self-assignment", but rather "since you explicitly say that aliasing cannot happen, and it's a program error if it does, you not only need to check for self-assignment, but you consequentially must fail hard if it happens".
And, insofar, it emphasizes a particular program logic and at the same time allows the compiler to optimize better for that particular special case, and thus does indeed make sense.
I belive what you guys are missing is that an argument to a member function could also alias parts or an object. Here's an example
struct some_class {
int some_value;
void compute_something(int& result) {
result = 2*some_value;
...
result -= some_value;
}
}
One would probably expect that to compile to
*(this + offsetof(some_value)) -> register1
2*register1 -> register2
...
register2 - register1 -> result
That code, unfortunately, would be wrong if someone passes a reference to some_value for result. Thus, a compiler would actually need to generate to following
*(this + offsetof(some_value)) -> register1
2*register1 -> register2
register2 -> result
...
*(this + offsetof(some_value)) -> register1
result -> register2
register2 - register1 -> register2
register2 -> result
which is obviously way less efficient. Note that unless compute_something is inlines, the compiler has no way of knowing whether result may alias some_value or not, so it has to assume the worst case, no matter no smart or dumb it is. So there a definite and very real advantage to restrict, even if applied to the this pointer.