有一个 Microsoft 特定扩展,可以像这样定义属性 getter 和 setter:
// declspec_property.cpp
struct S {
int i;
void putprop(int j) {
i = j;
}
int getprop() {
return i;
}
__declspec(property(get = getprop, put = putprop)) int the_prop;
};
int main() {
S s;
s.the_prop = 5;
return s.the_prop;
}
有没有办法用 clang 或 gcc 定义属性声明属性? 如果我搜索
__declspec
,我找到的只是 __declspec(dllexport)
,但我不是在寻找那个。
虽然 C++ 不提供对智能可重写运算符的支持(并且没有 gcc 扩展),但该语言允许您使用其现有功能来实现它。
以下示例(不假设涵盖所有情况!)显示了使用本机 C++ 11 或更高版本的可能解决方案。
我们可以使用虚拟重写来重写属性,但这不是现代智能属性在其他语言(例如 swift、C# 等)中的工作方式,因此,我使用 lambda 为 setter 和 getter 注入重写代码。
// The following is by no means a FULL solution!
#include <functional>
#include <iostream>
#include <cassert>
template<typename T>
class Property {
public:
Property(){}
operator const T& () const {
// Call override getter if we have it
if (getter) return getter();
return get();
}
const T& operator = (const T& other) {
// Call override setter if we have it
if (setter) return setter(other);
return set(other);
}
bool operator == (const T& other) const {
// Static cast makes sure our getter operator is called, so we could use overrides if those are in place
return static_cast<const T&>(*this) == other;
}
// Use this to always get without overrides, useful for use with overriding implementations
const T& get() const {
return t;
}
// Use this to always set without overrides, useful for use with overriding implementations
const T& set(const T& other) {
return t = other;
}
// Assign getter and setter to these properties
std::function<const T&()> getter;
std::function<const T&(const T&)> setter;
private:
T t;
};
// Basic usage, no override
struct Test {
Property<int> prop;
};
// Override getter and setter
struct TestWithOverride {
TestWithOverride(){
prop.setter = [&](const int& other){
std::cout << "Custom setter called" << std::endl;
return prop.set(other);
};
prop.setter = std::bind(&TestWithOverride::setProp,this,std::placeholders::_1);
prop.getter = std::bind(&TestWithOverride::getProp,this);
}
Property<int> prop;
private:
const int& getProp() const {
std::cout << "Custom getter called" << std::endl;
return prop.get();
}
const int& setProp(const int& other){
std::cout << "Custom setter called" << std::endl;
return prop.set(other);
}
};
int main(int,char**){
Test t;
TestWithOverride t1;
t.prop = 1;
assert(t.prop == 1);
t1.prop = 1;
assert(t1.prop == 1);
/*
Expected output:
1. No aborts on assertions
2. Text:
Custom setter called
Custom getter called
*/
return 0;
}
使用如下内容进行编译:
c++ -std=c++11 test.cpp -o test
./test
是的,
请参阅此链接
__declspec(property(get=..,put=..))
完全受 clang 支持,这是 gcc 对此 Microsoft 语言功能的支持的延续。
我一直在clang中使用它;它对于封装和重构来说非常棒。我帮助调试和推广了正确的 clang 实现。
它通过数组访问器属性进行了出色的优化。
foo[expr0][expr1] = expr2;
foo
在哪里
__declspec(property(put=foo_set)) foo_t foo[];
foo_t foo_set(T0 expr0, T1 expr1, foo_t expr2) {..}
它还可以与模板函数完美配合,使其成为高效重载和前向引用的理想选择。
template<typename T0, typename T1, typename foo_ta = foo_t>
foo_ta foo_set(T0 expr0, T1 expr1, foo_ta expr2) {..}
唯一的无赖是你不能使用现代的c++custom-attribute简写:
[[msvc::property(put = foo_set)]] foo_t foo[];
所以我使用这个模式:
[[msvc::property(put = foo_set)]] __declspec(property(put = foo_set))
foo_t foo[];
template<typename T0, typename T1, typename foo_ta = foo_t>
foo_ta foo_set(T0 expr0, T1 expr1, foo_ta expr2) {..}
或
template<bool fFwd=true>
bar_t bar_get() {
// reference any types declared later in your code
// template mechanics mean they will not be resolved until
// first **property** use
}
您不需要使用我上面展示的任何 template 用法或 array accessor 用法。我提供这些只是为了说明以上和其他可以使用属性做什么以及使用重载函数。
我使用
[[msvc::...]]
控制有关 -Wattributes
未定义的警告。有了这种模式,我的代码就可以为未来做好准备,并且读起来更干净、更一致。
给定属性仅适用于实例。将它们放置在类型上的技术是在类型上使用空单例:
struct T {
static inline struct K {
..declare properties on `k` here..
} k;
.. whatever you are doing with this `T` type.
};
现在您可以访问该类/静态属性:
T::k.property ..
Moshe Gottlieb 的出色答案可以通过以下代码进行改进,以在编译时允许只读或只写属性。
// Simulate Microsoft-specific extension:
// __declspec(property(get = getprop, put = putprop)) type propname
template<typename T, bool ReadOnly = false, bool WriteOnly = false>
class Property {
public:
Property(){}
operator const T& () const {
static_assert(!WriteOnly, "Cannot access write-only property.");
// Call override getter if we have it
if (getter) return getter();
return get();
}
const T& operator = (const T& other) {
static_assert(!ReadOnly, "Cannot set read-only property.");
// Call override setter if we have it
if (setter) return setter(other);
return set(other);
}
bool operator == (const T& other) const {
// Static cast makes sure our getter operator is called, so we could use overrides if those are in place
return static_cast<const T&>(*this) == other;
}
// Use this to always get without overrides, useful for use with overriding implementations
const T& get() const {
return t;
}
// Use this to always set without overrides, useful for use with overriding implementations
const T& set(const T& other) {
return t = other;
}
// Assign getter and setter to these properties
std::function<const T&()> getter;
std::function<const T&(const T&)> setter;
private:
T t;
};