SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

C++でプロパティを実現するココロミ

 使い方はシンプル、プロパティの持ち主となるクラスのコンストラクタ内でget/setを設定します。これを忘れるとプロパティにアクセスした途端bad_function_call例外が射出されるのでご注意を。

list-08
#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つ致命的な問題があるのにお気づきでしょうか。

list-09
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のコピーを抑止しておきましょう。

list-10
#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++プログラマへのささやかなお手伝いができればと考えています。どうぞよろしく。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
特集記事連載記事一覧

もっと読む

この記事の著者

επιστημη(エピステーメー)

C++に首まで浸かったプログラマ。Microsoft MVP, Visual C++ (2004.01~2018.06) "だった"りわんくま同盟でたまにセッションスピーカやったり中国茶淹れてにわか茶...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/7571 2014/01/28 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー