我想出了以下代码来实现C ++中的C#样式属性。它基本上是围绕typename T
的包装类,具有用户定义的get / set函数:
#include <functional>
template <typename T>
struct property
{
property(std::function<T(void)> getter, std::function<void(const T&)> setter)
: get(getter), set(setter) { }
property(std::function<T(void)> _getter)
: get(_getter), set([](const T&) { throw std::exception(); }) { }
property<T>& operator=(const T& value) { set(value); return *this; } // w i h o s h r ?
T operator=(const T& value) { set(value); return get(); } // h c t u e e e
operator T() { return get(); }
property<T>& operator=(property<T>& value) { set(value); return *this; }; //...
T operator=(property<T>& value) { set(value); return get(); }; //...
property<T>& operator=(property<T>&) = delete;
// arithmetic / assignment
#define OP(__OP__) \
T operator __OP__(const T& rhs) { return get() __OP__ rhs; } \
T operator __OP__##= (const T& rhs) { set(get() __OP__ rhs); return get(); } //...
//property<T>& operator __OP__##= (const T& rhs) { set(get() __OP__ rhs); return *this; } //...
OP(+); OP(-); OP(*); OP(/); OP(%); OP(^); OP(&); OP(|);
#undef OP
// bit shift
#define OP(__OP__) \
T operator __OP__(int rhs) { return get() __OP__ rhs; } \
property<T>& operator __OP__##= (int rhs) { set(get() __OP__ rhs); return *this; }
OP(<<); OP(>>);
#undef OP
// comparison / logic
#define OP(__OP__) bool operator __OP__(const T& rhs) { return get() __OP__ rhs;}
OP(<); OP(>); OP(==); OP(!=); OP(<=); OP(>=); OP(&&); OP(||);
#undef OP
// inc/dec
#define OP(__OP__) \
property<T>& operator __OP__##__OP__() { set(get() __OP__ 1); return *this; } \
T operator __OP__##__OP__(int) { T value = get(); operator __OP__##__OP__(); return value; }
OP(+); OP(-);
#undef OP
T operator ~() { return ~get(); }
bool operator !() { return !get(); }
private:
std::function<T(void)> get;
std::function<void(const T&)> set;
};
struct test
{
property<int> a = property<int>([&]()
{
return x/10;
},
[&](int value)
{
x = value*10;
});
property<int> b = property<int>([&]()
{
return y+10;
},
[&](int value)
{
y = value-10;
});
private:
int x, y;
};
using namespace std;
void main()
{
test x;
x.a = 5;
x.a = x.a + 15;
x.a *= 5;
x.b = x.a;
x.b += x.a / 10;
cout << "property a=" << dec << x.a << endl; // should be 100
cout << "property b=" << dec << x.b << endl; // should be 110
}
我的主要问题是,赋值运算符是否应返回T
或property<T>&
?返回引用似乎更好,但我想避免意外重新分配整个类(包括get / set函数),尽管我认为重载operator=(property<T>&)
并删除复制构造函数足以防止这种情况。
无论如何,两者似乎都产生了相同的结果,所以两者之间有什么区别吗?你更喜欢哪个?为什么?
编辑,相关问题:在未定义set
函数的情况下,是否有某种方法可以在编译时检测赋值,而不是抛出运行时异常?
答案 0 :(得分:0)
在大多数情况下,通过复制返回可以正常工作,但有一些不正常的情况,它不会。请考虑以下事项:
struct A {
A(int i) : i_{i} {}
int i_;
A operator=(A const& A) { i_ = a.i_; return *this; }
};
然后试试这个:
A a1{1};
A a2{2};
A a3{3};
(a3 = a2) = a1; // What is the value of a3::i_?
按副本返回意味着a3 = a2
赋值会生成一个重新分配为a1
的临时值。换句话说,a3::i_ == 2
分配后。如果您通过引用返回a3::i_ == 1
。