The following code is extracted from: https://github.com/facebook/folly/blob/master/folly/Synchronized.h
I recent had a look at the Folly library, and found something interesting. Consider the following example:
#include <iostream>
struct Lock {
void lock() {
std::cout << "Locking" << std::endl;
}
void unlock() {
std::cout << "Unlocking" << std::endl;
}
};
template <class T, class Mutex = Lock >
struct Synchronized {
struct LockedPtr {
explicit LockedPtr(Synchronized* parent) : p(parent) {
p->m.lock();
}
~LockedPtr() {
p->m.unlock();
}
T* operator->() {
std::cout << "second" << std::endl;
return &p->t;
}
private:
Synchronized* p;
};
LockedPtr operator->() {
std::cout << "first" << std::endl;
return LockedPtr(this);
}
private:
T t;
mutable Mutex m;
};
struct Foo {
void a() {
std::cout << "a" << std::endl;
}
};
int main(int argc, const char *argv[])
{
Synchronized<Foo> foo;
foo->a();
return 0;
}
The output is:
first
Locking
second
a
Unlocking
My question is: Why is this code valid? Does this pattern have a name?
The -> operator is called twice, but it has only been written once.
Because that's what the standard says:
1)
operator->
shall be a non-static member function taking no parameters. It implements class member access using-> postfix-expression -> id-expression
An expressionx->m
is interpreted as(x.operator->())->m
for a class objectx
of typeT
ifT::operator->()
exists and if the operator is selected as the best match function by the overload resolution mechanism (13.3).
(emphasis mine)
In your case, x
is foo
and m
is a()
, Now, Synchronized
overloads operator->
, foo->a()
is equivalent to:
(foo.operator->())->a();
foo.operator->()
is your overload in class Synchronized
, which returns a LockedPtr
, and then that LockedPtr
calls its own operator->
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With