为什么编译器调用 -> 运算符两次

Why does the compiler invoke the -> operator twice

本文关键字:两次 运算符 调用 编译器 gt 为什么      更新时间:2023-10-16
以下

代码提取自:https://github.com/facebook/folly/blob/master/folly/Synchronized.h

我最近看了一下Folly图书馆,发现了一些有趣的东西。请考虑以下示例:

#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;
}

输出为:

first
Locking
second
a
Unlocking

我的问题是:为什么这个代码有效?此模式有名称吗?

-> 运算符被调用两次,但只被写入过一次。

因为这就是标准所说的:

13.5.6 类成员访问 [over.ref]

1( operator->应为非静态成员函数,不采取 参数。它使用-> postfix-expression -> id-expression解释表达式x->m实现类成员访问 作为类对象的(x.operator->())->mx类型为 T 如果 T::operator->()存在,如果运算符被选为最佳运算符 通过过载解决机制 (13.3( 实现匹配功能。

(强调我的(

在您的情况下,xfoo的,ma()的,现在,Synchronized重载operator->foo->a()相当于:

(foo.operator->())->a();

foo.operator->()是你在类 Synchronized 中的重载,它返回一个LockedPtr,然后LockedPtr调用自己的operator->

问问自己:它还能怎么可能表现?

请记住,重载operator->的目的是使智能指针类可以使用与原始指针相同的语法。 也就是说,如果您有:

struct S
{
    T m;
};

并且您有一个指向S p的指针,然后您可以通过p->m访问S::m,无论pS*还是某种pointer_class<S>类型。

使用 -> 和直接调用operator->之间也有区别:

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

请注意,如果使用重载->不会自动降低额外级别,那么p->m可能意味着什么? 怎么可能使用过载?