C++ 为什么箭头 (->) 运算符的自动重新应用不适用于指针到指针类型?

C++ Why does the automatic re-application of the arrow (->) operator not apply to pointer-to-pointer types?

This question 讨论箭头运算符 -> 如何自动重新应用于重载 operator->() 的 return 值,直到值 returned是原始指针,此时原始指针被取消引用,就好像使用 -> 一样。但是,当他们希望取消对基值的引用时,情况就不一样了——他们必须使用 (*ptr_to_ptr)->foo() 来代替。在我看来,ptr_to_ptr->foo() 的用法是明确的,甚至比 -> 自动重新应用到 returned 值直到原始指针被 returned 更是如此.那么,这个决定背后的原因是什么?

最小工作示例:

#include <iostream>

struct Dog {
    void bark() { std::cout << "woof!" << std::endl; }
};

struct DogWalker {
    Dog* dog;
    Dog* operator->() {
        return dog;
    }
};

struct DogOwner {
    DogWalker walker = { new Dog() };
    DogWalker operator->() {
        return walker;
    }
};

void main()
{
    DogOwner owner;
    owner->bark(); // works, prints "woof"

    Dog** ptr_to_ptr = new Dog*;
    *ptr_to_ptr = new Dog;
    (**ptr_to_ptr).bark(); // works
    (*ptr_to_ptr)->bark(); // works
    //ptr_to_ptr->bark(); // ERROR
    //C2227: left of '->bark' must point to class/struct/union/generic type
}

该语言采用了 C 的大部分语义。应用于指针类型的 -> 运算符仅在指针指向 non-array 复合类型时才有效。由于 C 没有 classes,C++ 为重载 -> 定义了自己的语义,这对智能指针用例有意义。

您可以使用助手实现您想要的行为 class。

template <typename T>
struct Unwrap {
    T *p_;
    Unwrap (T *p = 0) : p_(p) {}
    T * operator -> () const { return p_; }
};

template <typename T>
struct Unwrap<T *> {
    T **p_;
    Unwrap (T **p = 0) : p_(p) {}
    Unwrap<T> operator -> () const { return *p_; }
};

template <typename T>
Unwrap<T> make_unwrap (T *p) { return p; }

然后你可以像这样使用它:

struct foo {
    void bar () { std::cout << "Hello\n"; }
};

int main () {
    foo p;
    auto pp = &p;
    auto ppp = &pp;
    auto pppp = make_unwrap(&ppp);

    pppp->bar();
}

Try it online!