这次这篇就讲一下怎么把之前定义的属性变平滑,我之前就说了,把能重载的运算符都重载一遍就行了
单目运算符:
#define OPERATOR1(op) \ template<class T> auto operator op (const Property<T>& l) -> decltype(op l->get()){ return op l.get(); } OPERATOR1(!); OPERATOR1(~); OPERATOR1(*); //不建议重载取地址运算符,如果你确实需要的话也可以重载 //OPERATOR1(&); #undef OPERATOR1
双目运算符:
#define OPERATOR2(op) \ template<class L, class R> auto operator op (const Property<L>& l, const R& r) -> decltype(l.get() op r) { return l.get() op r; } template<class L, class R> auto operator op (const Property<L>& l, const Property<R>& r) -> decltype(l.get() op r.get()) { return l.get() op r.get(); } OPERATOR2(+); OPERATOR2(-); OPERATOR2(*); OPERATOR2(/); OPERATOR2(%); OPERATOR2(>>); OPERATOR2(<<); OPERATOR2(|); OPERATOR2(||); OPERATOR2(&); OPERATOR2(&&); OPERATOR2(^); OPERATOR2(>); OPERATOR2(<); OPERATOR2(>=); OPERATOR2(<=); OPERATOR2(==); OPERATOR2(!=); #undef OPERATOR2
复合运算符:
#define OPERATOREX(op) \ template<class L,class R> Property<L>& operator op##=(Property<L>&l, const R& r) { auto v=l.get(); v op##= r; l.set(v); return l; } template<class L,class R> Property<L>& operator op##=(Property<L>&l, const Property<R>& r) { auto v=l.get(); v op##= r.get(); l.set(v); return l; } OPERATOREX(+); OPERATOREX(-); OPERATOREX(*); OPERATOREX(/); OPERATOREX(%); OPERATOREX(>>); OPERATOREX(<<); OPERATOREX(|); OPERATOREX(&); OPERATOREX(^); #undef OPERATOREX
自增自减运算符:
template<class T> auto operator++(Property<T>& l, int) -> decltype(l.get()++) { auto x = l.get(); auto ret = x++; l.set(x); return ret; } template<class T> auto operator++(Property<T>& l) -> decltype(++l.get()) { auto x = l.get(); ++x; l.set(x); return x; } template<class T> auto operator--(Property<T>& l, int) -> decltype(l.get()--) { auto x = l.get(); auto ret = x--; l.set(ret); return ret; } template<class T> auto operator--(Property<T>& l) -> decltype(--l.get()) { auto x = l.get(); --x; l.set(x); return x; }
重载中括号(写在类里面)
//作左值 template <class I> auto operator[](I i) -> decltype(_value[i])&{ return _value[i]; } //作右值 template <class I> auto operator[](I i) const -> const decltype(_value[i])& { return _value[i]; }
重载箭头(写在类里面)
const T operator->() const{ return get(); } T operator->() { return get(); }
重载圆括号(写在类里面)
template<class... Types> auto operator()(Types...args) -> decltype(_value(args...)){ return _value(args...); } template<class... Types> auto operator()(Types...args) const -> decltype(_value(args...)) { return _value(args...); }
好了,现在能重载的运算符就都重载了,剩下还有一些不能重载的运算符,那些也无能为力了,不过除了点号"."其余要么不常用,要么没重载的必要,而点号禁止重载我们也没办法,但是我们可以重载箭头运算符,我们可以在被包装类型不是指针型的时候用箭头进行被包装类型的成员调用,点号进行属性类型的成员调用。而被包装类型时指针时保持原含义。
那么上面的重载箭头需要改改了,运用模板类型的特化:
template<class T> class Property{ ... const T* operator->() const{ return &get(); } T* operator->() { return &get(); } ... } template<class _T> class Property<_T*>{ ... typedef _T* T; ... const T operator->() const{ return get(); } T operator->() { return get(); } ... }
不过这种写法有个缺点,就是如果被包装的类型重载了箭头运算符,那么将无法调用,虽然这种情况较少,但到底怎么重载箭头运算符还是看个人喜好吧。
关于属性的东西就暂时这样了,当然,还没完,还记得C#有个依赖项属性么,还有属性绑定之类的东西,不过那个东西需要处理事件,所以下一章不出意外的话就是类似Event或者Delegate之类的东西的实现。