C ++で自律型の `self`メンバー型を実装できますか?


101

C ++に、囲んでいるクラスのタイプに評価されるPHPのselfキーワードに相当するものがありません

クラスごとにそれを偽造するのは十分簡単です:

struct Foo
{
   typedef Foo self;
};

しかし、私はFoo再び書く必要がありました。多分私はいつかこれを間違えてサイレントバグを引き起こすでしょう。

とのいくつかの組み合わせを使用decltypeして、この作業を「自律的に」行うことはできますか?私はすでに以下を試しましたthis、その場所では無効です:

struct Foo
{
   typedef decltype(*this) self;
};

// main.cpp:3:22: error: invalid use of 'this' at top level
//     typedef decltype(*this) self;

(私はと同等ですstaticが、同じことを行いますが、遅延バインディングについては心配しません。)


9
this_tおそらく通常のC ++命名とより整合するでしょう。
Bartek Banachewicz 2014年

3
@BartekBanachewicz:またはthis_type
PlasmaHH

10
@プレトリアン、それが提案だったかどうか思い出せないが、誰かが提案auto()~auto()、俳優/ドクターのために。控えめに言っても興味深い。その目的で使用する場合、おそらくtypedef auto self;、それは私には少し大ざっぱに思えます。
クリス

11
正直なところ、これを可能にする構文を提案するのであればdecltype(class)、おそらくdecltype(struct)同等です。これautoは特定のコンテキストよりもはるかに明確であり、に基づく言語に適合させることによる問題はないと思いますdecltype(auto)
クリス

11
あなたがエラーを回避したいので、あなたは次のように、static_assertとダミー部材の機能を設定することもできますvoid _check() { static_assert(std::is_same<self&, decltype(*this)>::value, "Correct your self type"); }...が、クラステンプレートで動作しない
milleniumbug

回答:


38

Fooのタイプを繰り返さずにそれを行う方法は次のとおりです。

template <typename...Ts>
class Self;

template <typename X, typename...Ts>
class Self<X,Ts...> : public Ts...
{
protected:
    typedef X self;
};

#define WITH_SELF(X) X : public Self<X>
#define WITH_SELF_DERIVED(X,...) X : public Self<X,__VA_ARGS__>

class WITH_SELF(Foo)
{
    void test()
    {
        self foo;
    }
};

派生元にする場合は、次のようにFooマクロを使用する必要WITH_SELF_DERIVEDがあります。

class WITH_SELF_DERIVED(Bar,Foo)
{
    /* ... */
};

必要なだけの基本クラスで複数の継承を行うこともできます(可変個のテンプレートと可変個のマクロのおかげで)。

class WITH_SELF(Foo2)
{
    /* ... */
};

class WITH_SELF_DERIVED(Bar2,Foo,Foo2)
{
    /* ... */
};

これをgcc 4.8とclang 3.4で動作することを確認しました。


18
答えは「いいえ、でもラルフはできる!」;)
2014

3
これは単純にtypedefをそこに置くよりもどのように優れていますか?そして神様、なぜtypedefが必要なのでしょうか。どうして?
Miles Rout 2014年

7
@MilesRoutこれは質問についての質問であり、答えではありません。多くの場合、ソフトウェア開発(および特にメンテナンス)では、コードの冗長性を回避することが役立つため、ある場所で何かを変更しても、別の場所でコードを変更する必要はありません。以上が、autoおよびのdecltypeこの場合のポイントですself
ラルフタンデツキー2014年

1
template<typename T>class Self{protected: typedef T self;}; class WITH_SELF(Foo) : public Bar, private Baz {};より単純で、継承をより正確に制御できるようになります-反対の理由はありますか?
アコンカグア

@mmmmmmmm、「Do n't Repeat Yourself」の原則を深く理解することを学んでいない場合は、まだ十分に/真剣にコーディングしていない可能性があります。この "乱雑"(実際には遠い)は、洗練されていない言語機能(または機能の誤り、いくつかの厳密な手段による欠陥)について話す場合の非常にエレガントな修正です。
Sz。

38

可能な回避策(型を一度記述する必要があるため):

template<typename T>
struct Self
{
protected:
    typedef T self;
};

struct Foo : public Self<Foo>
{
    void test()
    {
        self obj;
    }
};

より安全なバージョンの場合、T実際に以下から派生することを保証できますSelf<T>

Self()
{
    static_assert(std::is_base_of<Self<T>, T>::value, "Wrong type passed to Self");
}

ことに注意してくださいstatic_assertタイプはに渡されるメンバ関数の内部には、おそらくチェックするための唯一の方法であるstd::is_base_of、完全でなければなりません。


4
typenametypedefでは必要ありません。そして、これは冗長性の数を減らさないので、私はそれが実行可能な代替であるとは思いません。
Konrad Rudolph

Foo名前を繰り返すというまったく同じ問題があります。
Bartek Banachewicz 2014年

6
これは、ある繰り返しが一緒に非常に近いので、しかし、わずかに優れた独創的なアプローチよりも。質問に対する解決策ではありませんが、最善の回避策で価値のある試みに対して+1を行います。
2014

4
私はそのソリューションを数回使用しましたが、それは1つの悪いことFooを持っています:から後で導出するとき、(1)Tを葉の子孫に上向きに伝播するか、(2)SelfTから何度も継承することを忘れないでください、または(3)すべての子がベースになることを受け入れます。使用可能ですが、かわいそうです。
ケツァルコアトル2014年

@quetzalcoatl:selfではなく複製しようとしているのでstatic、問題ありません。
2014

33

通常のクラス宣言の代わりにマクロを使用することができます。これにより、それが自動的に行われます。

#define CLASS_WITH_SELF(X) class X { typedef X self;

そして、次のように使用します

CLASS_WITH_SELF(Foo) 
};

#define END_CLASS }; おそらく読みやすさを助けるでしょう。


また、@ Paranaix Selfを使用して使用することもできます(実際にハッキングされ始めます)。

#define WITH_SELF(X) X : public Self<X>

class WITH_SELF(Foo) {
};

18
EWWWW END_CLASS。それは完全に不要です。
パピー

31
@DeadMG私は一部の人々がより一貫性を好むかもしれないと思います。結局のところ、最初のマクロの使用はで終わっていない{ので、}は「ハングしています」。
Bartek Banachewicz 2014年

6
いい考えですが、私は基本的にマクロに反対していませんが、C ++スコープを模倣した場合、つまり、それが次のように使用できる場合にのみ、ここでの使用を受け入れます。これはCLASS_WITH_SELF(foo) { … };達成不可能だと思います。
Konrad Rudolph

2
@KonradRudolph私もその方法を追加しました。ではない私は完全を期すために、それを好むこと
Bartek Banachewicz

1
ただし、このアプローチにはいくつかの問題があります。1つ目は、クラスを簡単に継承させることができません(別のマクロパラメータを使用しない限り)。2つ目は、継承するすべての問題を抱えていSelfます。
Bartek Banachewicz 14年

31

確かな証拠はありませんが、不可能だと思います。以下は失敗します-あなたの試みと同じ理由で-そして私はそれが私たちが得ることができる最も遠いものだと思います:

struct Foo {
    auto self_() -> decltype(*this) { return *this; }

    using self = decltype(self_());
};

基本的に、これが示すのは、typedefを宣言するスコープには、(直接または間接にかかわらず)へのアクセス権がないだけでありthis、クラスのタイプまたは名前に到達する他の(コンパイラーに依存しない)方法がないことです。


4
これはおそらくC ++ 1yの戻り型の控除となるのでしょうか?
dyp 2014年

4
@dyp何も変わらない私の答えの目的のため。ここでのエラーは、後続の戻り値の型ではなく、呼び出しにあります。
Konrad Rudolph

1
@quetzalcoatl:の内部decltypeは評価されていないコンテキストであるため、メンバー関数の呼び出しは問題ではありません(試行されません)
オービットでのライトネスレース

1
@TomKnapen clangで試してみてください。失敗します。それがGCCによって受け入れられているという事実は、私の知る限りバグです。

4
FWIW、struct S { int i; typedef decltype(i) Int; };にもかかわらず働くi非静的データメンバであること。decltype単純な名前が式として評価されないという特別な例外があるため、機能します。しかし、私はこの可能性を質問に答える方法で使用する方法を考えることはできません。

21

GCCとclangの両方で機能するのは、関数typedefのトレーリングリターンタイプでthis使用thisすることによって参照するtypedefを作成することです。これは静的メンバー関数の宣言ではないため、の使用thisは許容されます。その後、そのtypedefを使用して定義できますself

#define DEFINE_SELF() \
    typedef auto _self_fn() -> decltype(*this); \
    using self = decltype(((_self_fn*)0)())

struct Foo {
    DEFINE_SELF();
};

struct Bar {
    DEFINE_SELF();
};

残念ながら、規格を厳密に読むと、これでも無効であることがわかります。clangが行うことthisは、静的メンバー関数の定義で使用されていないことを確認することです。そして、ここでは、実際にはそうではありません。GCCはthis、関数の種類に関係なく、trailing-return-typeで使用されているかどうかを気にしませんstatic。メンバー関数でも使用できます。ただし、標準で実際に必要なのは、thisなのは、非静的メンバー関数(または非静的データメンバー初期化子)の定義の外では使用されないです。インテルはそれを正しく理解し、これを拒否します。

とすれば:

  • this 非静的データメンバーの初期化子と非静的メンバー関数([expr.prim.general] p5)でのみ許可されます。
  • 非静的データメンバーは、初期化子([dcl.spec.auto] p5)から推定される型を持つことはできません。
  • 非静的メンバー関数は、関数呼び出し([expr.ref] p4)のコンテキストで非修飾名によってのみ参照できます
  • 非静的メンバー関数は、未評価のコンテキストであっても、this使用できる場合は非修飾名でのみ呼び出すことができます([over.call.func] p3)。
  • 修飾名またはメンバーアクセスによる非静的メンバー関数への参照には、定義されている型への参照が必要です

self型の名前をどこかに含めない限り、実装する方法はまったくないと私は結論付けることができると思います。

編集:私の以前の推論には欠陥があります。「非静的メンバー関数は、これを使用できる場合([over.call.func] p3))に、未評価のコンテキストであっても、非修飾名でのみ呼び出すことができます」は正しくありません。それが実際に言うことは

キーワードthis(9.3.2)がスコープ内にあり、クラスT、またはの派生クラスを参照Tしている場合、暗黙のオブジェクト引数は(*this)です。キーワードthisがスコープ内にないか、別のクラスを参照している場合は、タイプの不自然なオブジェクトがT暗黙のオブジェクト引数になります。引数リストに不自然なオブジェクトが追加されており、オーバーロードの解決での非静的メンバー関数の1つが選択されている場合T、呼び出しは不正な形式です。

静的メンバー関数の内部にthisは表示されない場合がありますが、まだ存在しています。

ただし、コメントに従って、静的メンバー関数内では、f()to の変換は(*this).f()実行されず、実行されないため、[expr.call] p1に違反します。

[...]メンバー関数呼び出しの場合、後置式は暗黙的な(9.3.1、9.4)または明示的なクラスメンバーアクセス(5.2.5)であり、その[...]

メンバーのアクセスがないため。それでもうまくいきません。


[class.mfct.non-static] / 3は、それ_self_fn_1()がに「変換された」と言っていると思います(*this)._self_fn_1()。ただし、それが違法になるかどうかはわかりません。
dyp 2014年

@dyp「使用可能なXコンテキストでクラスのメンバーで使用されています」と書かれているthisので、変換は行われないと思います。

1
しかし、それは暗黙的または明示的なクラスメンバーアクセスではありません。【expr.call] / 1「メンバ関数呼び出しは、後置表現は、暗黙的または明示的なクラスメンバーアクセス[...]でなければならない」
DYP

(つまり、あなたが持っているとauto _self_fn_1() -> decltype(*this); auto _self_fn_1() const -> decltype(*this);どうなりますか?)
dyp 2014年

@dyp [expr.call] / 1は良い点です。詳しく調べる必要があります。constただし、オーバーロードについては問題ありません。5.1p3は、具体的には、静的メンバ関数に適用する修飾、及び種類は言うされていthisませんFoo*/ Bar*(なしconst何があるので、)constの宣言で_self_fn_2

17
#define SELF_CHECK( SELF ) void self_check() { static_assert( std::is_same< typename std::decay<decltype(*this)>::type, SELF >::value, "self wrong type" ); }
#define SELF(T) typedef T self; SELF_CHECK(T)

struct Foo {
  SELF(Foo); // works, self is defined as `Foo`
};
struct Bar {
  SELF(Foo); // fails
};

self_check呼び出されないため、これはテンプレートタイプでは機能しないため、static_assertは評価されません。

templatesでも機能するようにハックすることはできますが、実行時のコストはわずかです。

#define TESTER_HELPER_TYPE \
template<typename T, std::size_t line> \
struct line_tester_t { \
  line_tester_t() { \
    static_assert( std::is_same< decltype(T::line_tester), line_tester_t<T,line> >::value, "test failed" ); \
    static_assert( std::is_same< decltype(&T::static_test_zzz), T*(*)() >::value, "test 2 failed" ); \
  } \
}

#define SELF_CHECK( SELF ) void self_check() { static_assert( std::is_same< typename std::decay<decltype(*this)>::type, SELF >::value, "self wrong type" ); }

#define SELF(T) typedef T self; SELF_CHECK(T); static T* static_test_zzz() { return nullptr; }; TESTER_HELPER_TYPE; line_tester_t<T,__LINE__> line_tester

structサイズ1バイトの空がクラスに作成されます。タイプがインスタンス化selfされている場合、テストされます。


それも悪くない!
2014

@LightnessRacesinOrbitにtemplateクラスサポートオプションが追加されました。
Yakk-Adam Nevraumont 2014年

私は昨日仕事を辞めたとき、これについて正確に考えていました。あなたは私をそれに打ち負かした:)。リンクの問題を回避するために、self_check()をインラインとして宣言することをお勧めします(同じシンボルFoo :: self_check()が複数のオブジェクトファイルで見つかります)。

1
@theswine:9.3 / 2は、C ++標準の段落のインデックスです。これにより、クラス定義の本体で定義されたクラスメンバー関数がすでに暗黙的にであることを保証しinlineます。つまり、何も書く必要がないということですinline。したがって、inlineキャリア全体でこのようなクラスメンバー関数の定義の前で書いている場合は、ここで停止できます;)
オービットでのライトネスレース'16年

2
@LightnessRacesinOrbitああ、実際私はそうでした。ありがとう、それは将来私にいくつかのタイピングを保存します:)。私はC ++について知らないことがどれほどあるかにいつも驚いています。
2014年

11

また、それは不可能だと思います。失敗した別の問題がありますが、this-access を回避する興味深い試みです。

template<typename T>
struct class_t;

template<typename T, typename R>
struct class_t< R (T::*)() > { using type = T; };

struct Foo
{
   void self_f(); using self = typename class_t<decltype(&self_f)>::type;
};

#include <type_traits>

int main()
{
    static_assert( std::is_same< Foo::self, Foo >::value, "" );
}

C ++ではself_fアドレスを取得するときにクラスで修飾する必要があるため、これは失敗します:(


int T::*メンバー変数への通常のポインターでも同じ問題が発生します。そしてint self_var; typedef decltype(&self_var) self_ptr、どちらも動作しません、それは単なる通常のものint*です。
MSalters 2014年

9

私は最近、それ*thisが中括弧またはイコライザーで許可されていることを発見しました。(§5.1.1で説明したからn3337ワーキングドラフト):

3 [..]他のコンテキストのオブジェクト式とは異なり*this、メンバー関数本体の外側のクラスメンバーアクセス(5.2.5)のために完全な型である必要はありません。[..]

4それ以外の場合、member-declaratorがクラスXの非静的データメンバー(9.2)を宣言する場合、式thisはオプションのbrace-or-equal-initializer内の「pointer to X」型のprvalueです。メンバー宣言子の他の場所には表示されません。

5この表現thisは、他の文脈では使用されません。[ 例:

class Outer {
    int a[sizeof(*this)];               // error: not inside a member function
    unsigned int sz = sizeof(*this);    // OK: in brace-or-equal-initializer

    void f() {
        int b[sizeof(*this)];           // OK
        struct Inner {
            int c[sizeof(*this)];       // error: not inside a member function of Inner
        };
    }
};

例を終了 ]

それを念頭に置いて、次のコード:

struct Foo
{
    Foo* test = this;
    using self = decltype(test);

    static void smf()
    {
        self foo;
    }
};

#include <iostream>
#include <type_traits>

int main()
{
    static_assert( std::is_same< Foo::self, Foo* >::value, "" );
}

ダニエルフレイズ を渡しstatic_assertます。

Live example


testしかし、迷惑な役に立たない変数があります
MM

@Matt True、それでも私はそれを興味深いと感じました。

1
これはなし= thisでも機能するでしょう。そして、なぜだけではないusing self = Foo*;
user362515 2016年

1
ここでは確かに何も得られませんでした。なぜならtest、型であると宣言する必要があるからFoo *です。
ポールサンダース

4

型が外側のクラスのメンバー型である必要がない限り、の使用をselfで置き換えることができますdecltype(*this)。コードの多くの場所で使用する場合はSELF、次のようにマクロを定義できます。

#define SELF decltype(*this)

2
また、クラスの外やネストされたクラスでは使用できません
Drax

1
@Drax:クラスの外では利用できないことになっています。
Ben Voigt 2014年

@BenVoigtしかし、ネストされたクラスで使用できると思われますが、これはIMOで最も興味深いユースケースです。
のDrax

1
私はそうは思いません。self外側のクラスではなく、直接囲んでいるクラスを参照するべきではありませんか?しかし、私はphpをよく知りません。
Ben Voigt

1
@LightnessRacesinOrbit:コードとエラーは「PHPにはネストされた型がない」と言っているはずだと思いますか?
Ben Voigt 2014年

1

私のバージョンを提供してください。最良のことは、その使用がネイティブクラスと同じであることです。ただし、テンプレートクラスでは機能しません。

template<class T> class Self;

#define CLASS(Name) \
class Name##_; \
typedef Self<Name##_> Name; \
template<> class Self<Name##_>

CLASS(A)
{
    int i;
    Self* clone() const { return new Self(*this); }
};

CLASS(B) : public A
{
    float f;
    Self* clone() const { return new Self(*this); }
};

1

hvdの回答に基づいて、欠けているのは参照の削除だけであることがわかりました。そのため、std :: is_sameチェックは失敗します(b / c結果の型は、実際には型への参照です)。これで、このパラメーターなしのマクロですべての作業を実行できます。以下の作業例(私はGCC 8.1.1を使用しています)。

#define DEFINE_SELF \
    typedef auto _self_fn() -> std::remove_reference<decltype(*this)>::type; \
    using self = decltype(((_self_fn*)0)())

class A {
    public:
    DEFINE_SELF;
};

int main()
{
    if (std::is_same_v<A::self, A>)
        std::cout << "is A";
}

GCC以外のコンパイラではコンパイルできません。
zedu

0

「自分でやらなければならない」という明らかな解決策を繰り返します。これは、単純なクラスとクラステンプレートの両方で機能する、コードの簡潔なC ++ 11バージョンです。

#define DECLARE_SELF(Type) \
    typedef Type TySelf; /**< @brief type of this class */ \
    /** checks the consistency of TySelf type (calling it has no effect) */ \
    void self_check() \
    { \
        static_assert(std::is_same<decltype(*((TySelf*)(0))), \
            decltype(*this)>::value, "TySelf is not what it should be"); \
    } \
    enum { static_self_check_token = __LINE__ }; \
    static_assert(int(static_self_check_token) == \
        int(TySelf::static_self_check_token), \
        "TySelf is not what it should be")

ideone実際の動作を確認できます。この結果につながる起源は以下の通りです:

#define DECLARE_SELF(Type) typedef Type _TySelf; /**< @brief type of this class */

struct XYZ {
    DECLARE_SELF(XYZ)
};

これには、次のように、コードを別のクラスにコピーして貼り付け、XYZを変更するのを忘れるという明らかな問題があります。

struct ABC {
    DECLARE_SELF(XYZ) // !!
};

私の最初のアプローチはあまり独創的ではありませんでした-次のような関数を作成します:

/**
 *  @brief namespace for checking the _TySelf type consistency
 */
namespace __self {

/**
 *  @brief compile-time assertion (_TySelf must be declared the same as the type of class)
 *
 *  @tparam _TySelf is reported self type
 *  @tparam _TyDecltypeThis is type of <tt>*this</tt>
 */
template <class _TySelf, class _TyDecltypeThis>
class CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE;

/**
 *  @brief compile-time assertion (specialization for assertion passing)
 *  @tparam _TySelf is reported self type (same as type of <tt>*this</tt>)
 */
template <class _TySelf>
class CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<_TySelf, _TySelf> {};

/**
 *  @brief static assertion helper type
 *  @tparam n_size is size of object being used as assertion message
 *      (if it's a incomplete type, compiler will display object name in error output)
 */
template <const size_t n_size>
class CStaticAssert {};

/**
 *  @brief helper function for self-check, this is used to derive type of this
 *      in absence of <tt>decltype()</tt> in older versions of C++
 *
 *  @tparam _TyA is reported self type
 *  @tparam _TyB is type of <tt>*this</tt>
 */
template <class _TyA, class _TyB>
inline void __self_check_helper(_TyB *UNUSED(p_this))
{
    typedef CStaticAssert<sizeof(CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<_TyA, _TyB>)> _TyAssert;
    // make sure that the type reported as self and type of *this is the same
}

/**
 *  @def __SELF_CHECK
 *  @brief declares the body of __self_check() function
 */
#define __SELF_CHECK \
    /** checks the consistency of _TySelf type (calling it has no effect) */ \
    inline void __self_check() \
    { \
        __self::__self_check_helper<_TySelf>(this); \
    }

/**
 *  @def DECLARE_SELF
 *  @brief declares _TySelf type and adds code to make sure that it is indeed a correct one
 *  @param[in] Type is type of the enclosing class
 */
#define DECLARE_SELF(Type) \
    typedef Type _TySelf; /**< @brief type of this class */ \
    __SELF_CHECK

} // ~self

ちょっと長めですが、ご容赦ください。これは、関数がの型を推定するために使用さdecltypeれるので、C ++ 03なしで動作するという利点が__self_check_helperありthisます。また、はありませんstatic_assertが、sizeof()代わりにトリックが使用されます。C ++ 0xの場合は、もっと短くすることができます。現在、これはテンプレートでは機能しません。また、最後にセミコロンを期待していないマクロでマイナーな問題があり、杓子定規でコンパイルすれば、それは余分な不要なセミコロン(文句を言うだろうか、の体内にセミコロンで終わっていない奇妙な探してマクロで残されるXYZABC)。

Type渡されたのチェックDECLARE_SELFはオプションではありません。これはXYZABC(エラーがある)に気付かずにクラス(これで問題ありません)のみをチェックするためです。そして、それは私を襲った。テンプレートで機能する追加のストレージゼロコストソリューション:

namespace __self {

/**
 *  @brief compile-time assertion (_TySelf must be declared the same as the type of class)
 *  @tparam b_check is the asserted value
 */
template <bool b_check>
class CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2;

/**
 *  @brief compile-time assertion (specialization for assertion passing)
 */
template <>
class CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2<true> {};

/**
 *  @def DECLARE_SELF
 *  @brief declares _TySelf type and adds code to make sure that it is indeed a correct one
 *  @param[in] Type is type of the enclosing class
 */
#define DECLARE_SELF(Type) \
    typedef Type _TySelf; /**< @brief type of this class */ \
    __SELF_CHECK \
    enum { __static_self_check_token = __LINE__ }; \
    typedef __self::CStaticAssert<sizeof(CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2<int(__static_self_check_token) == int(_TySelf::__static_self_check_token)>)> __static_self_check

} // ~__self 

これは、一意の列挙値(または、すべてのコードを1行で記述しない場合は少なくとも一意)で静的アサーションを作成するだけであり、型を比較す​​るトリックは使用されず、テンプレートでも静的アサートとして機能します。そしてボーナスとして-最後のセミコロンが必要になりました:)。

良いインスピレーションを与えてくれたYakkに感謝します。最初に彼の答えを見ずにこれを書くことはありませんでした。

VS 2008およびg ++ 4.6.3でテスト済み。実際、XYZおよびのABC例では、文句を言います:

ipolok@ivs:~$ g++ self.cpp -c -o self.o
self.cpp:91:5: error: invalid application of âsizeofâ to incomplete type â__self::CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2<false>â
self.cpp:91:5: error: template argument 1 is invalid
self.cpp: In function âvoid __self::__self_check_helper(_TyB*) [with _TyA = XYZ, _TyB = ABC]â:
self.cpp:91:5:   instantiated from here
self.cpp:58:87: error: invalid application of âsizeofâ to incomplete type â__self::CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<XYZ, ABC

ABCをテンプレートにすると:

template <class X>
struct ABC {
    DECLARE_SELF(XYZ); // line 92
};

int main(int argc, char **argv)
{
    ABC<int> abc;
    return 0;
}

私たちは得るだろう:

ipolok@ivs:~$ g++ self.cpp -c -o self.o
self.cpp: In instantiation of âABC<int>â:
self.cpp:97:18:   instantiated from here
self.cpp:92:9: error: invalid application of âsizeofâ to incomplete type â__self::CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2<false>â

関数チェックがコンパイルされなかったため(予想どおり)、行番号チェックのみがトリガーされました。

C ++ 0xの場合(悪意のあるアンダースコアがない場合)、必要なのは次のとおりです。

namespace self_util {

/**
 *  @brief compile-time assertion (tokens in class and TySelf must match)
 *  @tparam b_check is the asserted value
 */
template <bool b_check>
class SELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE;

/**
 *  @brief compile-time assertion (specialization for assertion passing)
 */
template <>
class SELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<true> {};

/**
 *  @brief static assertion helper type
 *  @tparam n_size is size of object being used as assertion message
 *      (if it's a incomplete type, compiler will display object name in error output)
 */
template <const size_t n_size>
class CStaticAssert {};

#define SELF_CHECK \
    /** checks the consistency of TySelf type (calling it has no effect) */ \
    void self_check() \
    { \
        static_assert(std::is_same<TySelf, decltype(*this)>::value, "TySelf is not what it should be"); \
    }

#define DECLARE_SELF(Type) \
    typedef Type TySelf; /**< @brief type of this class */ \
    SELF_CHECK \
    enum { static_self_check_token = __LINE__ }; \
    typedef self_util::CStaticAssert<sizeof(SELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<int(static_self_check_token) == int(TySelf::static_self_check_token)>)> static_self_check

} // ~self_util

CStaticAssertビットは、テンプレート本体でtypedefされたタイプを生成するため、残念ながらまだ必要とされています(同じことをで行うことはできないと思いますstatic_assert)。このアプローチの利点は、依然としてコストがゼロであることです。


あなたは本質的にstatic_assertここで再実装していますね?さらに、不正な(予約された)識別子を使用しているため、完全なコードは無効です。
Konrad Rudolph

@KonradRudolphはい、そうです。現時点ではC ++ 0xがないため、完全な回答を提供するためにstatic_assertを再実装しました。私は答えでそれを言います。無効ですか?どのように指摘できますか?それはうまくコンパイルされました、私は今それを使っています。

1
C ++は、先行する下線とそれに続く大文字、およびグローバルスコープ内の2つの先行する下線ですべてをコンパイラーに予約するため、識別子は無効です。ユーザーコードはこれを使用してはなりませんが、すべてのコンパイラがエラーとしてフラグを立てるわけではありません。
Konrad Rudolph

@KonradRudolphなるほど、知りませんでした。私はそれを使用するコードをたくさん持っていますが、Linux / Mac / Windowsのどちらでも問題はありませんでした。しかし、私はそれを知るのは良いことだと思います。
2014年

0

私はこれらの奇抜なテンプレートについてすべてを知っているわけではありませんが、超単純なものはどうですか?

#define DECLARE_TYPEOF_THIS typedef CLASSNAME typeof_this
#define ANNOTATED_CLASSNAME(DUMMY) CLASSNAME

#define CLASSNAME X
class ANNOTATED_CLASSNAME (X)
{
public:
    DECLARE_TYPEOF_THIS;
    CLASSNAME () { moi = this; }
    ~CLASSNAME () { }
    typeof_this *moi;
    // ...
};    
#undef CLASSNAME

#define CLASSNAME Y
class ANNOTATED_CLASSNAME (Y)
{
    // ...
};
#undef CLASSNAME

いくつかのマクロを我慢できなければ、仕事は終わりました。を使用CLASSNAMEしてコンストラクタを宣言することもできます(もちろん、デストラクタも)。

ライブデモ


1
これは、クラスをどのように使用するか、または使用する必要があるかについて非常に顕著な影響を及ぼします
オービットのライトネスレース

@LightnessRacesinOrbitどうですか?見えない。振り返って、元の投稿の最後の文を削除しました。私がもともと持っていたものは、あなたがこれを考えるように導いたかもしれません。
ポールサンダース
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.