简体   繁体   中英

Why does the compiler invoke the -> operator twice

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:

13.5.6 Class member access [over.ref]

1) operator-> shall be a non-static member function taking no parameters. It implements class member access using -> postfix-expression -> id-expression An expression x->m is interpreted as (x.operator->())->m for a class object x of type T if T::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-> .

Ask yourself this: how else could it possibly behave?

Remember, the point of overloading operator-> at all is so that a smart pointer class can use the same syntax as a raw pointer. That is, if you have:

struct S
{
    T m;
};

and you have a pointer p to S , then you access S::m via p->m regardless of whether p is an S* or some pointer_class<S> type.

There's also the difference between using -> and invoking operator-> directly:

pointer_class<S> pointerObj;
S* p = pointerObj.operator->();

Note that if using an overloaded -> didn't automatically descend the extra level, what would p->m ever possibly mean? How could the overload ever be used?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM