我发现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>
有没有办法实现这个目标?正如文中提到的,我认为应该是可能的。
编辑:两个错误的代码版本。
error: ‘struct Shared<X>’ has no member named ‘g’
。
我读到了operator->
)error: result of ‘operator->()’ yields non-pointer result
)两个版本都相同,第17行或第18行已注释掉。
答案 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类中的虚拟内容,但我不确定这是否有效。)