为什么`T* operator->()` 即使写了一次也会重复应用?

Why the `T* operator->()` is applied repeatedly even if written once?

为什么T* operator->()写了一次就重复应用了?但是另一个T& operator*()应用一次,应该写很多次。

众所周知,C++ 中有 Execute-Around Pointer Idiom。 More C++ Idioms/Execute-Around Pointer

提供一个智能指针对象,它在对象的每个函数调用之前和之后透明地执行操作,前提是所有函数执行的操作都是相同的。并且每次处理前后都给一个class的成员变量。例如我们可以执行:

我在 main() 中添加了一些到 this example:

#include <iostream>
#include <vector>

class VisualizableVector {
  public:
    class proxy {
      public:
        proxy (std::vector<int> *v) : vect (v) {
            std::cout << "Before size is: " << vect->size() << std::endl;
        }
        std::vector<int> * operator -> () { return vect; }
        std::vector<int> & operator * () { return *vect; }
        ~proxy () { std::cout << "After size is: " << vect->size() << std::endl; }
      private:
        std::vector <int> * vect;
    };        
    VisualizableVector (std::vector<int> *v) : vect(v) {}            
    ~VisualizableVector () { delete vect; }   
    proxy operator -> () { return proxy (vect); }
    proxy operator * () { return proxy (vect); }
  private:
    std::vector <int> * vect;
};

int main()
{
  VisualizableVector vecc (new std::vector<int>);

  vecc->push_back (10);         // 1. Note use of -> operator instead of . operator      
  vecc->push_back (20);         // 2. ok      
  (*vecc)->push_back (30);      // 3. ok      
  // (*vecc).push_back (40);    // 4. error      
  (**vecc).push_back (50);      // 5. ok      
  // vecc->->push_back (60);    // 6. error     
}

在线编译结果:http://ideone.com/cXGdxW

为什么我们需要写两次**,而只写一次->

它的运算符return一样proxy:

    proxy operator -> () { return proxy (vect); }
    proxy operator * () { return proxy (vect); }

但是为什么我们需要再次使用*,而不应该再次使用->呢?:

  vecc->push_back (20);     // 2. ok      (vecc->) is proxy
  (**vecc).push_back (50);  // 5. ok      (*vecc) is proxy

为什么不vecc->->push_back (20);

标准 C++ (03/11/14) 中有关于此的内容吗?

更新:

在不同的情况下我们应该使用 1,2 或 3 operator->s : http://ideone.com/89kfYF

#include <iostream>
#include <vector>    
class VisualizableVector {
  public:
    class proxy {
      public:
        proxy (std::vector<int> *v) : vect (v) {
            std::cout << "Before size is: " << vect->size() << std::endl;
        }
        std::vector<int> * operator -> () { return vect; }
        std::vector<int> & operator * () { return *vect; }
        ~proxy () { std::cout << "After size is: " << vect->size() << std::endl; }
      private:
        std::vector <int> * vect;
    };        
    VisualizableVector (std::vector<int> *v) : vect(v) {}            
    ~VisualizableVector () { delete vect; }   
    proxy operator -> () { return proxy (vect); }
    proxy operator * () { return proxy (vect); }
  private:
    std::vector <int> * vect;
};

int main()
{
  VisualizableVector vecc (new std::vector<int>);

    vecc->push_back(30);            // ok       // one ->
  //vecc.operator->().push_back(30);// error    // one ->

  //vecc->->push_back(30);          // error    // two ->
  vecc.operator->()->push_back(30); // ok       // two ->

  auto proxy3 = vecc.operator->();      // 1st operator->()
  auto pointer = proxy3.operator->();   // 2nd operator->()
  pointer->push_back(30);               // 3rd operator->()      
  return 0;
}

第 327 页:Working Draft, Standard for Programming Language C++ 2014-11-19

13.5.6 Class member access [over.ref] 1 operator-> shall be a non-static member function taking no parameters. It implements the class member access syntax that uses ->. postfix-expression -> templateopt id-expression postfix-expression -> pseudo-destructor-name 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).

x->m(x.operator->())->m.

以下是这两个案例的细分:

(*vecc)->push_back(30);      // 3. ok      
VisualizableVector::proxy proxy3 = vecc.operator*();
std::vector<int> *pointer = proxy3.operator->();
pointer->push_back(30);

(**vecc).push_back(50);      // 5. ok
VisualizableVector::proxy proxy5 = vecc.operator*();
std::vector<int> &reference = proxy5.operator*();
reference.push_back(50);

您需要使用 * 两次取消引用的原因是因为 proxy::operator * () returns 指向基础类型的指针。

当你有一个指针时,你可以直接用“->”调用它的成员,或者你可以用“*”取消引用它然后使用“。”无论指针来自何处,这都是正确的。

因为你从 * 得到了指针,并且你在那个指针上使用了 *,所以你使用了两个 *。

a->b 被定义为 (*a).b 当且仅当 a 是一个指针。

如果a不是指针,则定义为(a.operator->())->b。现在通常 operator-> returns 一个指针,因此它会执行 (*(a.operator->())).b 并完成。

但如果它 returns 是一个非指针,则此定义是递归的。

没有类似的一元递归定义operator*

简而言之,标准是这么说的。为什么?因为作者认为它既优雅又实用。

顺便说一句,有一个针对 operator. 的活跃提案,到 2021 年可能会在 C++ 中出现。这将允许 (*a).b 的行为与 a->b 相同。