我可以在C ++中实现自治的`self`成员类型吗?


101

C ++ 缺少等效于PHP的selfkeyword 的关键字,该关键字的值取决于封闭类的类型。

在每个类别的基础上伪造它很容易:

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 2014年

10
@Praetorian,我不记得,如果它是一个提案或没有,但有人建议auto(),并~auto()为构建函数/ dtors。至少可以说有趣。如果用于此目的,也许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 2014年

Answers:


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
我猜答案是“不,但是拉尔夫可以!” ;)
Lightness Races in Orbit

3
这与简单地将typedef放入其中有什么优势?天哪,为什么还要使用typedef?为什么?
Miles Rout 2014年

7
@MilesRout这是关于问题的问题,而不是答案。在软件开发(尤其是维护)的许多情况下,避免代码中的冗余是有帮助的,因此,在一处更改某些内容不需要您在另一处更改代码。这就是autodecltype或在这种情况下的重点self
拉尔夫·坦德兹基

1
template<typename T>class Self{protected: typedef T self;}; class WITH_SELF(Foo) : public Bar, private Baz {};本来会更简单,并且可以更精确地控制继承-是否有反对的理由?
空加瓜

@mmmmmmmm,如果您还没有学会深刻地理解“不要重复自己”的原理,那么您可能还没有足够/认真地编写代码。在谈论不雅的语言功能(或功能不全,甚至某些严格的措施不足)的情况下,这种“混乱”(实际上远不是这样)是一个很好的解决方案。
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
不需要typename在typedef中。而且由于这不会减少冗余的数量,所以我认为这不是可行的选择。
康拉德·鲁道夫2014年

它具有完全相同的Foo名称重复问题。
Bartek Banachewicz 2014年

6
略好于原来的做法,不过,因为重复是非常接近。不是解决问题的方法,而是+1,以寻求最佳解决方案。
Lightness Races in Orbit

4
我使用了该解决方案几次,但它有一个缺点:当以后从派生时Foo,您必须:(1)将T向上传播到叶子后代,或者(2)记得多次从SelfT继承,或(3)接受所有子对象都是基本对象。可以使用,但不够漂亮。
quetzalcoatl 2014年

@quetzalcoatl:由于我想复制self而不是static,这没问题。
Lightness Races in Orbit

33

您可以使用宏代替常规的类声明,这将为您做到这一点。

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

然后用像

CLASS_WITH_SELF(Foo) 
};

#define END_CLASS }; 可能会提高可读性。


您也可以Self使用@Paranaix 并使用它(它开始变得很黑)

#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) { … };–并且我认为这是不可能实现的。
康拉德·鲁道夫2014年

2
@KonradRudolph我也添加了一种方法。不是为了完整而我喜欢它
Bartek Banachewicz 2014年

1
但是,这种方法存在一些问题。首先,它不允许您轻松地使类继承(除非您使用另一个宏参数),其次Self具有从其继承的所有问题。
Bartek Banachewicz 2014年

31

我没有肯定的证据,但我认为这是不可能的。由于与您的尝试相同的原因,以下操作失败了,我认为这是我们可以得到的最远的结果:

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

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

从本质上讲,这表明我们要声明我们的typedef的范围根本没有访问(直接或间接)的权限this,并且没有其他(与编译器无关的)获取类的类型或名称的方法。


4
这可能与C ++ 1y的返回类型推论有关吗?
dyp 2014年

4
@dyp就我的回答而言,它不会改变任何东西。这里的错误不在尾随返回类型中,而是在调用中。
康拉德·鲁道夫2014年

1
@quetzalcoatl:的内在条件decltype是未经评估的上下文,因此调用成员函数不是问题(不会尝试)
Lightness Races in Orbit 2014年

1
@TomKnapen使用clang尝试,它将失败。据我所知,它已被GCC接受是一个错误。

4
FWIW,struct S { int i; typedef decltype(i) Int; };即使i是非静态数据成员也可以工作。它之所以有效,是因为decltype有一个特殊的例外,其中简单名称不作为表达式求值。但是我无法想到以任何方式使用这种可能性来回答问题。

21

在GCC和clang中都起作用的是创建一个typedef,该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在尾随返回类型中使用它,而与函数的类型无关,它甚至允许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成为隐式对象参数。如果参数列表由伪造的对象扩展,并且重载解析选择的非静态成员函数之一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它说“ Xthis可以使用的上下文中用于类的成员”,所以我认为不执行转换。

1
但这既不是隐式的也不是显式的类成员访问。[expr.call] / 1“对于成员函数调用,后缀表达式应为隐式或显式的类成员访问[...]”
dyp 2014年

(我的意思是,当您拥有时会发生什么auto _self_fn_1() -> decltype(*this); auto _self_fn_1() const -> decltype(*this);?)
dyp 2014年

@dyp [expr.call] / 1是个不错的主意,我将不得不仔细研究。const不过,关于重载:这不是问题。对5.1p3进行了专门修改,使其也适用于静态成员函数,并说/ 的类型thisFoo*/ 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不会评估。

我们也可以进行一些修改以使其适用于template,但是运行时成本较低。

#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则进行测试。


也不错!
Lightness Races Orbit

@LightnessRacesinOrbit现在带有template类支持选项。
Yakk-Adam Nevraumont 2014年

昨天我下班时,我正在仔细考虑这个问题。你击败了我:)。我建议将self_check()声明为内联,以避免链接问题(在多个目标文件中找到相同的符号Foo :: self_check())。

1
@theswine:9.3 / 2是C ++标准中段落的索引,它保证在类定义主体中定义的类成员函数已经隐式地存在inline。这意味着您根本不需要编写inline。因此,如果您inline在整个职业生涯中一直在编写此类成员函数定义的文章之前,现在就可以停止;)
Lightness Races in Orbit 2014年

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否则,如果成员声明器声明了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, "" );
}

通过Daniel Frey的 static_assert

Live example


你有一个恼人的无用的变量test,虽然
MM

@Matt是的,但我仍然觉得很有趣。

1
如果没有= this,这本来可以工作的,对吗?而为什么不仅如此using self = Foo*;
user362515

1
我们在这里肯定没有得到任何东西,因为我们必须声明test类型为um,Foo *!。
保罗·桑德斯

4

除非类型必须封闭类的成员类型,你可以替换使用的selfdecltype(*this)。如果您在代码中的许多地方使用它,则可以定义一个宏SELF,如下所示:

#define SELF decltype(*this)

2
而且您不能在课外或嵌套课中使用它
Drax 2014年

1
@Drax:不应该在课外使用。
Ben Voigt 2014年

@BenVoigt但是它应该可以在嵌套类中使用,这是IMO最有趣的用例。
Drax 2014年

1
我不这么认为。不应该self引用直接封闭的类而不是外部类吗?但是我不太了解php。
Ben Voigt 2014年

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

这有点冗长,但是请在这里忍受。这样做的优点是可以在C ++ 03中工作,而无需使用decltype__self_check_helper函数来推导的类型this。同样,没有static_assert,但是使用了sizeof()技巧。您可以将其缩短为C ++ 0x。现在,这不适用于模板。此外,该宏还有一个小问题,即该宏最后不期望使用分号,如果使用pedantic进行编译,它将抱怨多余的分号(或者您会看到一个奇怪的宏,XYZ并且在和的主体中不以分号结尾ABC)。

Type传递给的进行检查DECLARE_SELF不是一种选择,因为那样只会检查XYZ该类(可以),而不检查ABC(有错误)。然后它就撞到了我。与模板一起使用的无附加存储零成本解决方案:

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 

这只是对唯一的枚举值进行静态声明(或者如果不将所有代码都写在一行上,则至少是唯一的),不使用任何类型比较技巧,并且即使在模板中也可用作静态声明。 。作为奖励-现在需要最后的分号:)。

我要感谢Yakk给我带来了很好的启发。在没有看到他的回答之前,我不会写这个。

已在VS 2008和g ++ 4.6.3中测试。确实,通过XYZABC示例,它抱怨:

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位,因为它会产生一个类型,该类型在模板主体中进行类型定义(我想不能用来完成static_assert)。这种方法的优点仍然是其零成本。


您实际上是static_assert在这里重新实现,不是吗?此外,由于您使用的是非法(保留)标识符,因此您的完整代码无效。
康拉德·鲁道夫2014年

@KonradRudolph是的,确实如此。我当时没有C ++ 0x,所以我重新实现了static_assert以提供完整的答案。我在回答中说。无效吗?您能指出如何吗?它编译良好,我现在正在使用它。
猪2014年

1
标识符是无效的,因为C ++为编译器保留了所有带有引号的下划线,大写字母以及全局范围内的两个引号。用户代码不得使用它,但是并非所有编译器都将其标记为错误。
Konrad Rudolph 2014年

@KonradRudolph我明白了,我不知道。我有很多使用它的代码,在Linux / Mac / Windows上都从未遇到过问题。但是我想知道是一件好事。

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
它对如何使用/必须使用该类产生了显着影响
Lightness Races in Orbit

@LightnessRacesinOrbit怎么回事?我没看到 经过反思,我确实删除了原始帖子的最后一句话。我最初在那里所拥有的东西可能使您想到了这一点。
保罗·桑德斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.