【问题标题】:Why the `T* operator->()` is applied repeatedly even if written once?为什么即使只写一次,`T* operator->()` 也会重复应用?
【发布时间】:2016-07-23 14:17:55
【问题描述】:

为什么T* operator->()即使写一次也会重复应用?但是另一个T& operator*()是申请一次,应该写多次。

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

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

  • 锁定互斥锁
  • 记录操作
  • 可视化不断变化的数据

我在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

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

它的操作符返回同样的东西proxy:

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

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

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

为什么不vecc-&gt;-&gt;push_back (20);

在标准 C++ (03/11/14) 中有什么关于这个的吗?

更新:

在不同的情况下,我们应该使用 1,2 或 3 operator-&gt;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 类成员访问 [over.ref] 1 operator-> 应为不带参数的非静态成员函数。它实现了 使用 -> 的类成员访问语法。后缀表达式-> templateopt id-expression 后缀表达式 -> 伪析构函数名称 对于一个类,表达式 x->m 被解释为 (x.operator->())->m 如果 T::operator->() 存在并且操作符是 T 类型的对象 x 被重载决议选为最佳匹配函数 机制(13.3)。

x-&gt;m(x.operator-&gt;())-&gt;m

【问题讨论】:

  • 简短的回答是:因为这就是 C++ 的工作方式,因为这就是您编写的类的工作方式。
  • 旁注:这是一个糟糕的问题,因为不必要地使用了“新”(没有与“删除”配对)
  • 恕我直言,operator-&gt;operator* 返回完全相同的类型,这本身就是不好的。

标签: c++ c++11 design-patterns dereference idioms


【解决方案1】:

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

如果a不是指针,则定义为(a.operator-&gt;())-&gt;b。现在通常operator-&gt; 返回一个指针,所以它然后执行(*(a.operator-&gt;())).b 并完成。

但是如果它返回一个非指针,这个定义是递归的。

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

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

顺便说一句,有一个针对 operator. 的积极提案,可能会在 2021 年使用 C++。这将允许 (*a).b 的行为与 a-&gt;b 相同。

【讨论】:

【解决方案2】:

以下是对这两种情况进行了一些细分:

(*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 * () 返回一个指向底层类型的指针。

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

既然你从 * 中得到了指针,并且你在那个指针上使用了 *,这就是你使用两个 * 的原因。

【讨论】:

  • 抱歉,我覆盖了某人的同时编辑,试图处理格式并使我的符号保持一致。你能不能再把 * 转一下,或者告诉我怎么做?
  • 谢谢。是的,如果我实现强制转换运算符proxy::operator std::vector&lt;int&gt;&amp;() { return *vect; } 并使用隐式/显式static_cast,我可以避免双重**。例如隐式std::vector&lt;int&gt;&amp; vec_ref = *vecc; 或显式static_cast&lt;std::vector&lt;int&gt;&gt;(*vecc).push_back (40); // 4.a ok ideone.com/IlvhPc
猜你喜欢
  • 1970-01-01
  • 2019-10-17
  • 1970-01-01
  • 2010-09-12
  • 1970-01-01
  • 2016-04-05
  • 1970-01-01
  • 1970-01-01
  • 2018-01-08
相关资源
最近更新 更多