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

时间:2018-04-06 22:42:10

标签: c++ language-design

This question讨论了箭头运算符->如何自动重新应用于重载operator->()的返回值,直到返回的值是原始指针,此时原始指针被解除引用,就像使用->一样。但是,如果有一个指针到指针类型,他们希望取消引用基值,则情况不一样 - 它们必须使用(*ptr_to_ptr)->foo()。在我看来,ptr_to_ptr->foo()的用法将是明确的,甚至比返回值的->自动重新应用直到返回原始指针更为明确。那么,这个决定背后的原因是什么?

最小的工作示例:

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

1 个答案:

答案 0 :(得分:2)

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

您可以使用帮助程序类实现所需的行为。

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!