Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the compiler invoke the -> operator twice

Tags:

c++

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.

like image 620
Allan Avatar asked Sep 11 '12 08:09

Allan


1 Answers

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->.

like image 65
Luchian Grigore Avatar answered Sep 24 '22 06:09

Luchian Grigore