代理包装类,operator->链接

时间:2014-09-22 13:09:54

标签: c++

我发现this paper实现了一个C ++代理包装器类,它通过重载operator->允许在每个方法调用之前和之后执行代码。

在第11节“技术”中,提到可以将包装类链接到彼此,例如

#include <iostream>
using namespace std;

template<class T, class Pref, class Suf> class Wrap;
template<class T, class Suf>
class Call_proxy {
  T* p;
  mutable bool own;
  Suf suffix;
  Call_proxy(T* pp, Suf su) :p(pp) , own(true) , suffix(su) { } // restrict creation
  Call_proxy& operator=(const Call_proxy&) ; // prevent assignment
  public:
  template<class U, class P, class S> friend class Wrap;
  Call_proxy(const Call_proxy& a) : p(a.p) , own(true) , suffix(a.suffix) { a.own=false; }
  ~Call_proxy() { if (own) suffix() ; }

  T* operator->() const{ return p;} // error: ‘struct Shared<X>’ has no member named ‘g’
  //T& operator->() const{ return *p;} // error: result of ‘operator->()’ yields non-pointer result
};


template<class T, class  Pref, class Suf>
class  Wrap {
  T& p;
  Pref prefix;
  Suf suffix;
public:
  Wrap(T& x,  Pref pr, Suf su) :p(x) , prefix(pr) , suffix(su) { }
  Call_proxy<T,Suf> operator->() const{ 
      prefix() ; 
      return  Call_proxy<T,Suf>(&p,suffix); 
  }
};

void prefix() { cout << "prefix "; }
void suffix() { cout << " suffix\n"; }
struct Pref { void operator()() const{ cout<< " Pref "; } };
struct Suf { void operator()() const{ cout<< " Suf "; } };

template<class T> struct Shared : public Wrap<T,Pref, Suf> {
   Shared(T& obj) : Wrap<T,Pref, Suf>(obj,Pref() , Suf()) { }
};
template<class T> struct Tracer : public Wrap<T,void(*)() ,void(*)()>  { 
    Tracer(T& x) : Wrap<T,void(*)() ,void(*)()>(x,::prefix,::suffix) { } 
};

class X {
public:
    void g() const{ cout << "g()"; }
};

int main() {// test program
  X x;
  Shared<X> xx(x) ;
  Tracer<Shared<X>> xxx(xx);

  xx->g();
  xxx->g();
  return 0;
}

但这失败了,错误为error: ‘struct Shared<X>’ has no member named ‘g’。 我阅读了有关operator->重载并了解问题的信息(Call_proxy返回一个指针,它不会传播重载,如上所述here)。

我通过返回引用而不是指针尝试了一些替代方法,但最后我遇到了上面提到的问题,或者在引用类型(operator->)上调用error: result of ‘operator->()’ yields non-pointer result的问题。 / p>

有没有办法实现这个目标?正如文中提到的,我认为应该是可能的。


编辑:两个错误的代码版本。

两个版本都相同,第17行或第18行已注释掉。

3 个答案:

答案 0 :(得分:1)

operator->必须返回指针类型或带有重载operator->的类类型。当它返回具有重载operator->的类类型时,会发生链接。当它返回指针时,链接终止。

如果Call_proxy::operator->返回T*,那么这些链的评估如下:

   Shared<X> xx(x) ;
// xx->g();
   xx.Wrap<X,Pref,Suf>::operator->()   // Returns Call_proxy<X,Suf>
     .Call_proxy<X,Suf>::operator->()  // Returns X*
     ->X::g();

   Tracer<Shared<X>> xxx(xx);
// xxx->g();
   xxx.Wrap<X,void(*)(),void(*)()>::operator->()  // Returns Call_proxy<X,void(*)(),void(*)()>
      .Call_proxy<X,void(*)()>::operator->()      // Returns Shared<X>*
      ->Shared<X>::g();

第一次调用没问题,第二次调用失败,因为链接终止于Shared<X>*Shared<X>没有g()

如果Call_proxy::operator->返回T&,则链的评估如下:

   Shared<X> xx(x) ;
// xx->g();
   xx.Wrap<X,Pref,Suf>::operator->()   // Returns Call_proxy<X,Suf>
     .Call_proxy<X,Suf>::operator->()  // Returns X&
     .X::operator->(); // ???

   Tracer<Shared<X>> xxx(xx);
// xxx->g();
   xxx.Wrap<X,void(*)(),void(*)()>::operator->()  // Returns Call_proxy<X,void(*)(),void(*)()>
      .Call_proxy<X,void(*)()>::operator->()      // Returns Shared<X>&
      .Shared<X>::operator->()                    // Call_proxy<X,Suf>
      .Call_proxy<X,Suf>::operator->()            // Returns X&
      .X::operator->(); // ???

两个调用都失败因为X既不是指针类型也不是重载operator->的类类型。

要使此设计有效,Call_proxy::operator->需要以某种方式区分T是否为代理类,如果是,则返回T&,如果是T*则返回{{1}}不是。

答案 1 :(得分:0)

正如我的问题所述,并由@Oktalist详细阐述,问题在于区分具有operator->的类和不具有template<class T> struct Sealed { T&p; Sealed(T&x ) : p(x){} T* operator->() {return &p;} }; 的类。有两种方法。

第一个也是最简单的解决方案是拥有一个受控层次结构,其中最内层的类总是返回一个指针类型。实现看起来像这样:

std::shared_ptr<T>

如果拥有T,则可以使用int main() { X x; Sealed<X> xx(x); Shared<Sealed<X>> xxx(xx) ; Tracer<Shared<Sealed<X>>> xxxx(xxx); auto y = std::make_shared<X>(); Shared<decltype(y)> yy(y); Tracer<decltype(yy)>yyy(yy); } ,这具有相同的效果。

Call_proxy

第二个选项是让operator->具有不同的实现,具体取决于是否T类具有operator->的实现。在我看来,这对用户来说更优雅,更不容易出错,但实施起来更复杂。另外,我没有找到一种方法来实现这个没有C ++ 11功能,不幸的是我无法使用它。

该实现使用SFINAE检查程序检查某个类是否存在template <typename T> class has_operator { typedef char yes; typedef char no[2]; template <typename I> static I identity(I); template<typename U,U> struct Check; template <typename C> static yes& test(Check<decltype(identity(&C::operator->)),&C::operator-> >*); template <typename C> static no& test(...); public: enum { value = sizeof(test<T>(0)) == sizeof(yes) }; };

template<class T, bool hasOperator = true/*has_operator<T>::value*/>
struct PtrOperator {
  T* p;
  PtrOperator(T*pp) : p(pp) {}
  T* operator->() const { return p;}
};

template<class T> 
struct PtrOperator<T,true>{
  T* p;
  PtrOperator<T,true>(T*pp) : p(pp) {}
  T& operator->() const { return *p;}
};

template<class T, class  Pref, class Suf> class  Wrap;
template<class T, class Suf>
class  Call_proxy : public PtrOperator<T>{
  mutable bool own;
  Suf suffix;
  Call_proxy(T* pp, Suf su) : PtrOperator<T>(pp) , own(true) , suffix(su) { }  // restrict creation
  Call_proxy& operator=(const  Call_proxy&) ;  // prevent assignment
public:
  template<class  U, class  P, class S> friend class  Wrap;
  Call_proxy(const  Call_proxy& a) : PtrOperator<T>(a.p) , own(true) , suffix(a.suffix) { a.own=false; }
  ~Call_proxy() { if (own) suffix() ; }
};

取决于结果,Call_proxy可以是模板专用的;这可以转移到顶级。

operator->

使用它,最后不需要特殊的类。可以包装任何对象,int main() { Shared<X> z(x) ; Tracer<Shared<X>> zz(z); x.g(); z->g(); zz->g(); } 的链接在所有情况下都有效。

{{1}}

我已将完整示例上传到http://ideone.com/byQYR1

答案 2 :(得分:-1)

您的Wrapper运算符 - &gt;由默认函数由Tracer类隐藏。

您可以按照此处的说明解决此问题: Why does an overloaded assignment operator not get inherited?

(您也可以尝试将操作符&gt;函数设置为Wrap类中的虚拟内容,但我不确定这是否有效。)