使い方はシンプル、プロパティの持ち主となるクラスのコンストラクタ内でget/setを設定します。これを忘れるとプロパティにアクセスした途端bad_function_call例外が射出されるのでご注意を。
#include <iostream>
#include <string>
using namespace std;
class Person {
private:
int age_;
std::string name_;
public:
Person() {
// get/set のセットアップ
Age( [this](){return age_; },
[this](const int& value) { return age_ = value; });
Name([this](){return name_; },
[this](const std::string& value) { return name_ = value; });
}
Person::Person(const Person& other) : Person() { // delegating constructor (C++11)
Age = other.Age;
Name = other.Name;
}
// property age/name
property<int> Age;
property<std::string> Name;
std::ostream& print_on(std::ostream& out) const { return out << name_ << ':' << age_; }
};
using namespace std;
ostream& operator<<(ostream& stream, const Person& p) {
return p.print_on(stream);
}
int main() {
Person adam;
adam.Name = "Adam";
adam.Age = 20;
cout << adam << endl;
string name = adam.Name;
int age = adam.Age;
cout << name << ':' << age << endl << endl;
}
N1615ではメンバ関数を呼び出しましたが、この実装ではstd::function<>を利用してget/setを外出しにしたってことですね。
なかなかイイ感じで動いてくれてますが、1つ致命的な問題があるのにお気づきでしょうか。
int main() {
Person adam;
adam.Name = "Adam";
adam.Age = 20;
Person eve;
eve.Name = "Eve";
eve.Age = 18;
cout << "ADAM-> "; adam.print_on(cout) << endl;
cout << "EVE -> "; eve.print_on(cout) << endl;
cout << "copy adam to eve, and set eve's age to 19" << endl;
eve = adam;
eve.Age = 19;
cout << "ADAM-> "; adam.print_on(cout) << endl;
cout << "EVE -> "; eve.print_on(cout) << endl;
cout << endl;
}
/* 実行結果:
ADAM-> Adam:20
EVE -> Eve:18
copy adam to eve, and set eve's age to 19
ADAM-> Adam:19
EVE -> Adam:20
*/
あらら、adamをeveにコピーした後eveの年齢をいじったらadamの方が書き換わっちゃいました。
原因は明らか。Personがコピーされるに伴い、内包するpropertyも当然コピーされるわけですが、このときadam側にあるpropertyのget/setがコピーされちゃうからですね(RWPropertyでも持ち主が入れ替わって同様の問題が発生します)。property::operator=()を再定義し、set/setのコピーを抑止しておきましょう。
#include <functional>
template<class T>
class property {
public:
// 以下3行追加
property() = default;
property(const property& other) { }
property& operator=(const property& other) { set(other.get()); return *this; }
void operator()(const std::function<T()>& getter, const std::function<T(const T&)>& setter) {
if ( !get ) get = getter;
if ( !set ) set = setter;
}
T operator()() const { return get(); }
T operator()(T const& value) { return set(value); }
operator T() const { return get(); }
T operator=(T const& value) { return set(value); }
typedef T value_type;
private:
std::function<T()> get;
std::function<T(const T&)> set;
};
新春一発目はstd::function<>を使ったプロパティの実装でした。
今年もまたこんな調子でC++プログラマへのささやかなお手伝いができればと考えています。どうぞよろしく。
