3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

C++でC#の自動実装プロパティっぽいもの

Last updated at Posted at 2021-12-29

概要

ファントムタイプの投稿を組み合わせていい感じにしました。
宣言は一行かつ、なるべく普通の変数と同じようなアクセスができることを目指しました。
先に使い方をのせます。

使い方

usage.cpp
#include <vector>
struct Hoge : public PropertyEnabled<Hoge> {
public:
    // ≒ public int Num { get; private set; }
    PublicGetPrivateSet<int> Num;

    // ≒ public List<int> Ints { get; private set; }
    PublicGetPrivateSet<std::vector<int>> Ints{ 1,3 };

    // ≒ private List<int> intsC ;
    //   public ReadOnlyCollection<int> IntsC => intsC.AsReadOnly();
    ReadonlyPublicGetPrivateSet<std::vector<int>> IntsR{ 3,4,5 };

    //クラス内からのアクセス
    void Fuga() {
        //Num
        int num = Num;                  //ok     暗黙の変換
        Num = 4;                        //ok     代入

        //Ints
        std::vector<int> ints = Ints;   //ok     暗黙の変換
        Ints.push_back(3);              //ok     非constメンバアクセス
        Ints[0] = 2;                    //ok     非constインデックスアクセス
        Ints = ints;                    //ok     代入

        //IntsR
        std::vector<int> intsC = IntsR; //ok     暗黙の変換
        IntsR.push_back(2);             //ok     非constメンバアクセス
        IntsR[0] = 2;                   //ok     非constインデックスアクセス
        IntsR = intsC;                  //ok     代入
    }
};
//クラス外からのアクセス
int main() {
    Hoge h;

    //Num
    int num = h.Num;                  //ok     暗黙の変換
    h.Num = 4;                        //error  代入

    //Ints
    std::vector<int> ints = h.Ints;   //ok     暗黙の変換
    h.Ints.push_back(3);              //ok     非constメンバアクセス
    h.Ints[0] = 2;                    //ok     非constインデックスアクセス
    h.Ints = ints;                    //error  代入

    //IntsR
    std::vector<int> intsC = h.IntsR; //error  暗黙の変換 (private継承してるので関数を通さないとerror)
    intsC = h.IntsR.Get();            //ok     アクセス(関数経由)
    h.IntsR->size();                  //ok     constメンバアクセス(演算子経由)
    auto s = h.IntsR.At(0);           //ok     constインデックスアクセス(関数経由)

    h.IntsR = intsC;                  //error  代入
    h.IntsR->push_back();             //error  非constメンバアクセス(演算子経由してもダメ)
    h.IntsR.At(0) = 2;                //error  非constインデックスアクセス(関数経由してもダメ)
}

コメントを見ればだいたい分かると思うのでReadonlyPublicGetPrivateSetだけ説明します。(長いけど他に思いつかなかった)
PublicGetPrivateSetで宣言するとそのクラスの外からの代入はエラーにできますが、メンバ変数の書き換えや非constメンバ関数の呼び出しは防げません。
ReadonlyPublicGetPrivateSetはこれを防ぐためにprivate継承を使って外からはconstアクセスだけに制限したものです。

環境

C++20
Visual Studio 2019 16.11.8

実装

・CRTPのためにネストさせる。
・型引数が基本型(intやfloatなど)かどうかで分ける部分をFundamentalBaseNotFundamentalBaseに実装。
・同じ名前で使えるように継承とクラステンプレートの部分特殊化を使ってPublicGetPrivateSetにまとめる。
・基本型じゃない場合ReadonlyにできるようにNotFundamentalBaseをprivate継承してReadonlyPublicGetPrivateSetを実装。

という風にしました。最終的にこんな感じです。

template<class Owner>
class PropertyEnabled {
private:
    /// <summary>
    /// _Innerが基本型なのでメンバ変数にもつ
    /// </summary>
    template<class _Inner>
    requires std::is_fundamental_v<_Inner>
    class FundamentalBase {
        //...
    };
    /// <summary>
    /// _Innerが基本型じゃないので継承する
    ///</summary>
    template <class _Inner>
    requires (!std::is_fundamental_v<_Inner>)
    class NotFundamentalBase :public _Inner {
        //...
    };
public:
    //部分特殊化を使って同じ名前でどんな型でも使えるようにする
    template<class _Inner>
    class PublicGetPrivateSet;

    template<class _Inner>
    requires std::is_fundamental_v<_Inner>
    class PublicGetPrivateSet<_Inner> :public FundamentalBase<_Inner> {
        //...
    };

    template <class _Inner>
    requires (!std::is_fundamental_v<_Inner>)
    class PublicGetPrivateSet<_Inner> :public NotFundamentalBase<_Inner> {
        //...
    };

    /// <summary>
    /// クラス外からのconstアクセス関数を実装
    /// </summary>
    template <class _Inner>
    requires (!std::is_fundamental_v<_Inner>)
    class ReadonlyPublicGetPrivateSet :private NotFundamentalBase<_Inner> {
        //...
    };
};

基本型を型引数にとる場合のベースクラス

ほとんど先駆者のものと同じです。
それにくわえて代入にかかわる演算子も沢山オーバーロードして使いやすくしました。

/// <summary>
/// _Innerが基本型なのでメンバ変数にもつ
/// 基本型なので値渡し
/// </summary>
template<class _Inner>
requires std::is_fundamental_v<_Inner>
    class FundamentalBase {
    public:
        using Owner = _Owner;
        using Outer = PropertyEnabled<Owner>;
        using Inner = _Inner;
        using This = FundamentalBase<Inner>;

    private:
        Inner inner;

    protected:
        constexpr  FundamentalBase()noexcept : inner() {
        }
        constexpr  FundamentalBase(Inner t)noexcept : inner(t) {
        }
    public:
        constexpr virtual ~FundamentalBase()noexcept {
        }

        //getter
        constexpr operator Inner () const {
            return inner;
        }
    protected:
        constexpr Inner& GetRef() {
            return inner;
        }
        //setter
        This& operator=(Inner r) {
            this->inner = r;
            return *this;
        }
        //setter
        This& operator=(This r) {
            this->inner = r.inner;
            return *this;
        }

        //あとは利便性のために演算子オーバーロード

        This& operator++() {
            ++this->inner;
            return *this;
        }
        This& operator--() {
            --this->inner;
            return *this;
        }
        This operator++(int) {
            auto temp = *this;
            ++this->inner;
            return temp;
        }
        This operator--(int) {
            auto temp = *this;
            --this->inner;
            return temp;
        }
        This& operator+=(Inner r) {
            this->inner += r;
            return *this;
        }
        This& operator-=(Inner r) {
            this->inner -= r;
            return *this;
        }
        This& operator*=(Inner r) {
            this->inner *= r;
            return *this;
        }
        This& operator/=(Inner r) {
            this->inner /= r;
            return *this;
        }
        This& operator%=(int r) {
            this->inner %= r;
            return *this;
        }
        This& operator<<=(int r) {
            this->inner <<= r;
            return *this;
        }
        This& operator>>=(int r) {
            this->inner >>= r;
            return *this;
        }
};

operator<=>?

三方比較演算子と言って比較演算子を自動定義してくれます。
暗黙的キャストするのでいらなかったため削除。

基本型じゃない型を型引数にとる場合のベースクラス

型引数を継承して上手いことやります。

/// <summary>
/// _Innerが基本型じゃないので継承する
/// ※virtualデストラクタがないクラスでアップキャストしてdeleteすると未定義
///</summary>
template <class _Inner>
requires (!std::is_fundamental_v<_Inner>)
    class NotFundamentalBase :public _Inner {
    public:
        using Owner = _Owner;
        using Outer = PropertyEnabled<Owner>;
        using Inner = _Inner;
        using This = NotFundamentalBase<Inner>;

    protected:
        using Inner::Inner;

        //メンバイニシャライザのために追加
        template <class... Args>
        requires std::constructible_from<Inner, Args...>
            constexpr NotFundamentalBase(Args&&... args) :Inner(std::forward<Args>(args)...) {
        }

    public:
        constexpr virtual ~NotFundamentalBase()noexcept {
        }

    protected:
        constexpr Inner& GetRef() {
            return *this;
        }
        //setter
        This& operator=(const Inner& r) {
            ((Inner)*this) = r;
            return *this;
        }
        //setter
        This& operator=(const This& r) {
            ((Inner)*this) = r;
            return *this;
        }
};

コンストラクタについて

ファントムタイプの投稿にあるように、{ } でコンストラクタを呼んだ場合initializer_listになるのか {第一引数,第二引数...} となるのか分かりにくいことがあります。参考にした投稿では静的メソッドで生成していましたが、initializer_listを受け取ったものの構築できないときにstatic_assertをだすことで対応しました。

よく考えたらusingでコンストラクタを継承するだけで上手くいったので修正しました。
参考にした投稿ではexplicitを付けるために複雑になってたみたいです。

ただ継承したコンストラクタだけだとメンバイニシャライザで呼ぶ際エラーになったので1つだけ残しています。

何でもかんでも継承することについて

virtualデストラクタがないクラスも継承してしまうため、そのクラスにアップキャストしてdeleteすると未定義動作になります。具体的にはstd::vectorなどで、

未定義動作
//子クラスでnewして暗黙的にアップキャスト
std::vector<int>* v= new NotFundamentalBase<std::vector<int>>();
delete v;//親クラスのポインタをdelete  <-未定義動作 どうなるか分からない

みたいなかんじでポリモーフィズムとかに使うと危ないみたいですが、単に自動実装プロパティとして使うなら問題ないはずです。

同じ名前でどんな型でも非Readonlyで使えるようにする

public継承してfriendするだけですが、親のコンストラクタと代入演算子は自動的に継承されないので個別にusingします。
もっと簡潔に書く方法あるのかも・・・

//部分特殊化を使って同じ名前でどんな型でも使えるようにする
template<class _Inner>
class PublicGetPrivateSet;

template<class _Inner>
requires std::is_fundamental_v<_Inner>
    class PublicGetPrivateSet<_Inner> :public FundamentalBase<_Inner> {
    public:
        using Inner = _Inner;
        using Base = FundamentalBase<Inner>;
        using This = PublicGetPrivateSet<Inner>;

        friend typename std::enable_if<std::is_class<Owner>::value, Owner>::type;

    protected:
        using Base::FundamentalBase;
        using Base::operator=;
        PublicGetPrivateSet() = default;
        This& operator=(This&) = default;
        This& operator=(This&&) = default;
};

template <class _Inner>
requires (!std::is_fundamental_v<_Inner>)
    class PublicGetPrivateSet<_Inner> :public NotFundamentalBase<_Inner> {
    public:
        using Inner = _Inner;
        using Base = NotFundamentalBase<Inner>;
        using This = PublicGetPrivateSet<Inner>;

        friend typename std::enable_if<std::is_class<Owner>::value, Owner>::type;

    protected:
        using Base::NotFundamentalBase;
        using Base::operator=;
        PublicGetPrivateSet() = default;
        This& operator=(This&) = default;
        This& operator=(This&&) = default;
};

基本型じゃない型を型引数にとってReadonlyにするクラス

まず、private継承を使うことでプロパティを宣言したクラスの外からアクセスできなくします。ただ、constアクセスだけは許可したいので専用の演算子や関数を実装します。

このときoperator[]を使ってしまうとクラス内からも非constアクセスができなくなってしまうので注意。また、operator*operator()にしてもいいですが可読性を考えて関数にしています。

/// <summary>
/// NotFundamentalBaseからprivate継承
/// クラス外からのconstアクセス関数を実装
/// </summary>
template <class _Inner>
requires (!std::is_fundamental_v<_Inner>)
    class ReadonlyPublicGetPrivateSet :private NotFundamentalBase<_Inner> {
    public:
        using Owner = _Owner;
        using Outer = PropertyEnabled<Owner>;
        using Inner = _Inner;
        using Base = NotFundamentalBase<Inner>;
        using This = ReadonlyPublicGetPrivateSet<Inner>;

        friend typename std::enable_if<std::is_class<Owner>::value, Owner>::type;

    protected:
        using Base::NotFundamentalBase;
        using Base::operator=;
        ReadonlyPublicGetPrivateSet() = default;
        This& operator=(This&) = default;
        This& operator=(This&&) = default;

    public:
        //本体にアクセス
        const Inner& Get()const noexcept {
            return *this;
        }
        //メンバアクセス
        const Inner* operator->()const noexcept {
            return this;
        }
        //インデックスアクセス
        template<typename U>
        auto At(const U& idx)const {
            return ((const Inner&)*this)[idx];
        }
};

追記 ProtectedSet版

欲しくなったので追加。
PrivateSet版を継承するのは設計的にどうかと思ったのでコピペしてfriendを追加しました。
子クラスからはPropertyEnabled<Owner>::Access<T>を経由して非constアクセスします。

追加にあたってFundamentalBaseNotFundamentalBaseクラスの関数を増やし、下のコード全文も更新しました。

template<class Owner>
class PropertyEnabled {
private:
///ベースクラス
public:

///ここにPrivateSet版

    //ProtectedSet版
    //ProtectedSet版
    template<class _Inner>
    class PublicGetProtectedSet;

    template<class _Inner>
    requires std::is_fundamental_v<_Inner>
        class PublicGetProtectedSet<_Inner> :public FundamentalBase<_Inner> {
        public:
            using Inner = _Inner;
            using Base = FundamentalBase<Inner>;
            using This = PublicGetProtectedSet<Inner>;

            friend typename std::enable_if<std::is_class<Owner>::value, Owner>::type;
            friend PropertyEnabled<Owner>;

        protected:
            using Base::FundamentalBase;
            using Base::operator=;
            PublicGetProtectedSet() = default;
            This& operator=(This&) = default;
            This& operator=(This&&) = default;
    };

    template <class _Inner>
    requires (!std::is_fundamental_v<_Inner>)
        class PublicGetProtectedSet<_Inner> :public NotFundamentalBase<_Inner> {
        public:
            using Inner = _Inner;
            using Base = NotFundamentalBase<Inner>;
            using This = PublicGetProtectedSet<Inner>;

            friend typename std::enable_if<std::is_class<Owner>::value, Owner>::type;
            friend PropertyEnabled<Owner>;

        protected:
            using Base::NotFundamentalBase;
            using Base::operator=;
            PublicGetProtectedSet() = default;
            This& operator=(This&) = default;
            This& operator=(This&&) = default;
    };

    /// <summary>
    /// NotFundamentalBaseからprivate継承
    /// クラス外からのconstアクセス関数を実装
    /// </summary>
    template <class _Inner>
    requires (!std::is_fundamental_v<_Inner>)
        class ReadonlyPublicGetProtectedSet :private NotFundamentalBase<_Inner> {
        public:
            using Owner = _Owner;
            using Outer = PropertyEnabled<Owner>;
            using Inner = _Inner;
            using Base = NotFundamentalBase<Inner>;
            using This = ReadonlyPublicGetProtectedSet<Inner>;

            friend typename std::enable_if<std::is_class<Owner>::value, Owner>::type;
            friend PropertyEnabled<Owner>;

        protected:
            using Base::NotFundamentalBase;
            using Base::operator=;
            ReadonlyPublicGetProtectedSet() = default;
            This& operator=(This&) = default;
            This& operator=(This&&) = default;

        public:
            //本体にアクセス
            const Inner& Get()const noexcept {
                return *this;
            }
            //メンバアクセス
            const Inner* operator->()const noexcept {
                return this;
            }
            //インデックスアクセス
            template<typename U>
            auto At(const U& idx)const {
                return ((const Inner&)*this)[idx];
            }
    };

protected:

    //子クラスからProtectedSet版へのアクセス
    template <class T>
    requires std::same_as< Owner, typename T::Owner>
        auto Access(T& target)->T::Inner& {
        return target.GetRef();
    }
};

コード全文

長いもののわりと読みやすく書けたと思うのですがどうでしょう?

長いので省略
#pragma once

#include <concepts>
#include <initializer_list>
#include <utility>

template<class _Owner>
class PropertyEnabled {
public:
    using Owner = _Owner;
    using Outer = PropertyEnabled<Owner>;

private:
    /// <summary>
    /// _Innerが基本型なのでメンバ変数にもつ
    /// 基本型なので値渡し
    /// </summary>
    template<class _Inner>
    requires std::is_fundamental_v<_Inner>
        class FundamentalBase {
        public:
            using Owner = _Owner;
            using Outer = PropertyEnabled<Owner>;
            using Inner = _Inner;
            using This = FundamentalBase<Inner>;

        private:
            Inner inner;
        protected:
            constexpr  FundamentalBase()noexcept : inner() {
            }
            constexpr  FundamentalBase(Inner t)noexcept : inner(t) {
            }
        public:
            constexpr virtual ~FundamentalBase()noexcept {
            }

            //getter
            constexpr operator Inner () const {
                return inner;
            }
        protected:
            constexpr Inner& GetRef() {
                return inner;
            }
            //setter
            This& operator=(Inner r) {
                this->inner = r;
                return *this;
            }
            //setter
            This& operator=(This r) {
                this->inner = r.inner;
                return *this;
            }

            //あとは利便性のために演算子オーバーロード

            This& operator++() {
                ++this->inner;
                return *this;
            }
            This& operator--() {
                --this->inner;
                return *this;
            }
            This operator++(int) {
                auto temp = *this;
                ++this->inner;
                return temp;
            }
            This operator--(int) {
                auto temp = *this;
                --this->inner;
                return temp;
            }
            This& operator+=(Inner r) {
                this->inner += r;
                return *this;
            }
            This& operator-=(Inner r) {
                this->inner -= r;
                return *this;
            }
            This& operator*=(Inner r) {
                this->inner *= r;
                return *this;
            }
            This& operator/=(Inner r) {
                this->inner /= r;
                return *this;
            }
            This& operator%=(int r) {
                this->inner %= r;
                return *this;
            }
            This& operator<<=(int r) {
                this->inner <<= r;
                return *this;
            }
            This& operator>>=(int r) {
                this->inner >>= r;
                return *this;
            }
    };
    /// <summary>
    /// _Innerが基本型じゃないので継承する
    /// ※virtualデストラクタがないクラスでアップキャストしてdeleteすると未定義
    ///</summary>
    template <class _Inner>
    requires (!std::is_fundamental_v<_Inner>)
        class NotFundamentalBase :public _Inner {
        public:
            using Owner = _Owner;
            using Outer = PropertyEnabled<Owner>;
            using Inner = _Inner;
            using This = NotFundamentalBase<Inner>;

        protected:
            using Inner::Inner;

            //メンバイニシャライザのために追加
            template <class... Args>
            requires std::constructible_from<Inner, Args...>
                constexpr NotFundamentalBase(Args&&... args) :Inner(std::forward<Args>(args)...) {
            }

        public:
            constexpr virtual ~NotFundamentalBase()noexcept {
            }

        protected:
            constexpr Inner& GetRef() {
                return *this;
            }
            //setter
            This& operator=(const Inner& r) {
                ((Inner)*this) = r;
                return *this;
            }
            //setter
            This& operator=(const This& r) {
                ((Inner)*this) = r;
                return *this;
            }
    };

public:

    //部分特殊化を使って同じ名前でどんな型でも使えるようにする
    template<class _Inner>
    class PublicGetPrivateSet;

    template<class _Inner>
    requires std::is_fundamental_v<_Inner>
        class PublicGetPrivateSet<_Inner> :public FundamentalBase<_Inner> {
        public:
            using Inner = _Inner;
            using Base = FundamentalBase<Inner>;
            using This = PublicGetPrivateSet<Inner>;

            friend typename std::enable_if<std::is_class<Owner>::value, Owner>::type;

        protected:
            using Base::FundamentalBase;
            using Base::operator=;
            PublicGetPrivateSet() = default;
            This& operator=(This&) = default;
            This& operator=(This&&) = default;
    };

    template <class _Inner>
    requires (!std::is_fundamental_v<_Inner>)
        class PublicGetPrivateSet<_Inner> :public NotFundamentalBase<_Inner> {
        public:
            using Inner = _Inner;
            using Base = NotFundamentalBase<Inner>;
            using This = PublicGetPrivateSet<Inner>;

            friend typename std::enable_if<std::is_class<Owner>::value, Owner>::type;

        protected:
            using Base::NotFundamentalBase;
            using Base::operator=;
            PublicGetPrivateSet() = default;
            This& operator=(This&) = default;
            This& operator=(This&&) = default;
    };

    /// <summary>
    /// NotFundamentalBaseからprivate継承
    /// クラス外からのconstアクセス関数を実装
    /// </summary>
    template <class _Inner>
    requires (!std::is_fundamental_v<_Inner>)
        class ReadonlyPublicGetPrivateSet :private NotFundamentalBase<_Inner> {
        public:
            using Owner = _Owner;
            using Outer = PropertyEnabled<Owner>;
            using Inner = _Inner;
            using Base = NotFundamentalBase<Inner>;
            using This = ReadonlyPublicGetPrivateSet<Inner>;

            friend typename std::enable_if<std::is_class<Owner>::value, Owner>::type;

        protected:
            using Base::NotFundamentalBase;
            using Base::operator=;
            ReadonlyPublicGetPrivateSet() = default;
            This& operator=(This&) = default;
            This& operator=(This&&) = default;

        public:
            //本体にアクセス
            const Inner& Get()const noexcept {
                return *this;
            }
            //メンバアクセス
            const Inner* operator->()const noexcept {
                return this;
            }
            //インデックスアクセス
            template<typename U>
            auto At(const U& idx)const {
                return ((const Inner&)*this)[idx];
            }
    };



    //ProtectedSet版
    template<class _Inner>
    class PublicGetProtectedSet;

    template<class _Inner>
    requires std::is_fundamental_v<_Inner>
        class PublicGetProtectedSet<_Inner> :public FundamentalBase<_Inner> {
        public:
            using Inner = _Inner;
            using Base = FundamentalBase<Inner>;
            using This = PublicGetProtectedSet<Inner>;

            friend typename std::enable_if<std::is_class<Owner>::value, Owner>::type;
            friend PropertyEnabled<Owner>;

        protected:
            using Base::FundamentalBase;
            using Base::operator=;
            PublicGetProtectedSet() = default;
            This& operator=(This&) = default;
            This& operator=(This&&) = default;
    };

    template <class _Inner>
    requires (!std::is_fundamental_v<_Inner>)
        class PublicGetProtectedSet<_Inner> :public NotFundamentalBase<_Inner> {
        public:
            using Inner = _Inner;
            using Base = NotFundamentalBase<Inner>;
            using This = PublicGetProtectedSet<Inner>;

            friend typename std::enable_if<std::is_class<Owner>::value, Owner>::type;
            friend PropertyEnabled<Owner>;

        protected:
            using Base::NotFundamentalBase;
            using Base::operator=;
            PublicGetProtectedSet() = default;
            This& operator=(This&) = default;
            This& operator=(This&&) = default;
    };

    /// <summary>
    /// NotFundamentalBaseからprivate継承
    /// クラス外からのconstアクセス関数を実装
    /// </summary>
    template <class _Inner>
    requires (!std::is_fundamental_v<_Inner>)
        class ReadonlyPublicGetProtectedSet :private NotFundamentalBase<_Inner> {
        public:
            using Owner = _Owner;
            using Outer = PropertyEnabled<Owner>;
            using Inner = _Inner;
            using Base = NotFundamentalBase<Inner>;
            using This = ReadonlyPublicGetProtectedSet<Inner>;

            friend typename std::enable_if<std::is_class<Owner>::value, Owner>::type;
            friend PropertyEnabled<Owner>;

        protected:
            using Base::NotFundamentalBase;
            using Base::operator=;
            ReadonlyPublicGetProtectedSet() = default;
            This& operator=(This&) = default;
            This& operator=(This&&) = default;

        public:
            //本体にアクセス
            const Inner& Get()const noexcept {
                return *this;
            }
            //メンバアクセス
            const Inner* operator->()const noexcept {
                return this;
            }
            //インデックスアクセス
            template<typename U>
            auto At(const U& idx)const {
                return ((const Inner&)*this)[idx];
            }
    };

protected:

    //子クラスからProtectedSet版へのアクセス
    template <class T>
    requires std::same_as< Owner, typename T::Owner>
        auto Access(T& target)->T::Inner& {
        return target.GetRef();
    }
};

終わりに

マクロに逃げたり宣言2行で妥協したりconst偽装を試したりしましたが、最終的にわりといいかんじになったんじゃないかと思います。

visual studio2019だとrequires文の次の行が勝手にインデントされるのなんとかならないものか・・・

3
4
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?