我可以在 C + + 中实现一个自治的“自我成员”类型吗?

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的等效性,它也是如此,只是具有后期绑定。)

13354 次浏览

I have no positive evidence but I think it’s impossible. The following fails – for the same reason as your attempt – and I think that’s the furthest we can get:

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


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

Essentially, what this demonstrates is that the scope at which we want to declare our typedef simply has no access (be it direct or indirect) to this, and there’s no other (compiler independent) way of getting to the class’ type or name.

A possible workaround (as you still have to write the type once):

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


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

For a more safer version we could assure that T actually derives from Self<T>:

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

Notice that a static_assert inside a member function is probably the only way to check, as types passed tostd::is_base_of have to be complete.

You can use a macro instead of a regular class declaration, that will do that for you.

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

And then use like

CLASS_WITH_SELF(Foo)
};

#define END_CLASS }; would probably help readability.


You could also take @Paranaix's Self and use it (it starts to get really hackish)

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


class WITH_SELF(Foo) {
};

I also think it's impossible, here's another failed but IMHO interesting attempt which avoids the 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, "" );
}

which fails because C++ requires you to qualify self_f with the class when you want to take it's address :(

Unless the type needs to be member type of the enclosing class you could replace the use of self with decltype(*this). If you use it in many places in your code you can define a macro SELF as follows:

#define SELF decltype(*this)
#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
};

this does not work on template types, as self_check is not called, so the static_assert is not evaluated.

We can do some hacks to make it work for templates as well, but it has a minor run time cost.

#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

an empty struct of size 1 byte is created in your class. If your type is instantiated, self is tested against.

What works in both GCC and clang is to create a typedef that refers to this by using this in the trailing-return-type of a function typedef. Since this is not the declaration of a static member function, the use of this is tolerated. You can then use that typedef to define self.

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


struct Foo {
DEFINE_SELF();
};


struct Bar {
DEFINE_SELF();
};

Unfortunately, a strict reading of the standard says that even this is not valid. What clang does is check that this is not used in the definition of a static member function. And here, it indeed isn't. GCC doesn't mind if this is used in a trailing-return-type regardless of the sort of function, it allows it even for static member functions. However, what the standard actually requires is that this is not used outside of the definition of a non-static member function (or non-static data member initialiser). Intel gets it right and rejects this.

Given that:

  • this is only allowed in non-static data member initialisers and non-static member functions ([expr.prim.general]p5),
  • non-static data members cannot have their type deduced from the initialiser ([dcl.spec.auto]p5),
  • non-static member functions can only be referred to by an unqualified name in the context of a function call ([expr.ref]p4)
  • non-static member functions can only be called by unqualified name, even in unevaluated contexts, when this can be used ([over.call.func]p3),
  • a reference to a non-static member function by qualified name or member access requires a reference to the type being defined

I think I can conclusively say that there is no way at all to implement self without including in some way, somewhere, the type name.

Edit: There is a flaw in my earlier reasoning. "non-static member functions can only be called by unqualified name, even in unevaluated contexts, when this can be used ([over.call.func]p3)," is incorrect. What it actually says is

If the keyword this (9.3.2) is in scope and refers to class T, or a derived class of T, then the implied object argument is (*this). If the keyword this is not in scope or refers to another class, then a contrived object of type T becomes the implied object argument. If the argument list is augmented by a contrived object and overload resolution selects one of the non-static member functions of T, the call is ill-formed.

Inside a static member function, this may not appear, but it still exists.

However, per the comments, inside a static member function, the transformation of f() to (*this).f() would not be performed, and it that isn't performed, then [expr.call]p1 is violated:

[...] For a member function call, the postfix expression shall be an implicit (9.3.1, 9.4) or explicit class member access (5.2.5) whose [...]

as there would be no member access. So even that wouldn't work.

Here's how you can do it without repeating the type of 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;
}
};

If you want to derive from Foo then you should use the macro WITH_SELF_DERIVED in the following way:

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

You can even do multiple inheritance with as many base classes as you want (thanks to variadic templates and variadic macros):

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


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

I have verified this to work on gcc 4.8 and clang 3.4.

I will repeat the obvious solution of "having to do it yourself". This is the succinct C++11 version of the code, which works with both simple classes and class templates:

#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")

You can see it in action at ideone. The genesis, leading to this result is below:

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


struct XYZ {
DECLARE_SELF(XYZ)
};

This has the obvious problem with copy-pasting the code to a different class and forgetting to change XYZ, like here:

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

My first approach was not very original - making a function, like this:

/**
*  @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

It is kind of lengthy, but please bear with me here. This has the advantage of working in C++03 without decltype, as the __self_check_helper function is employed to deduce type of this. Also, there is no static_assert, but the sizeof() trick is employed instead. You could make it much shorter for C++0x. Now this will not work for templates. Also, there is a minor issue with the macro not expecting semicolon at the end, if compiling with pedantic, it will complain about an extra unnecessary semicolon (or you will be left with an odd looking macro not ending in semicolon in the body of XYZ and ABC).

Making a check on the Type that is passed to DECLARE_SELF is not an option, as that would only check the XYZ class (which is ok), oblivious to ABC (which has error). And then it hit me. A no-additional storage zero-cost solution that works with templates:

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

This simply makes static assertion on a unique enum value (or at least unique in case you don't write all of your code on a single line), no type-comparing trickery is employed, and it works as static assert, even in templates. And as a bonus - the final semicolon is now required :).

I'd like to thank Yakk for giving me a good inspiration. I wouldn't write this without first seeing his answer.

Tested with VS 2008 and g++ 4.6.3. Indeed, with the XYZ and ABC example, it complains:

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>â

Now if we make ABC a template:

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


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

We will get:

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>â

Only the line-number check triggered, as the function check was not compiled (as expected).

With C++0x (and without the evil underscores), you would need just:

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

I believe that the CStaticAssert bit is regrettably still required as it produces a type, which is typedef-ed in the template body (i suppose the same cannot be done with static_assert). The advantage of this approach is still its zero cost.

Provide my version. The best thing is that its use is the same as the native class. However, it doesn't work for template classes.

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); }
};

I recently discovered that *this is allowed in a brace-or-equal-initializer. Described in § 5.1.1 (from the n3337 working draft):

3 [..] Unlike the object expression in other contexts, *this is not required to be of complete type for purposes of class member access (5.2.5) outside the member function body. [..]

4 Otherwise, if a member-declarator declares a non-static data member (9.2) of a class X, the expression this is a prvalue of type “pointer to X” within the optional brace-or-equal-initializer. It shall not appear elsewhere in the member-declarator.

5 The expression this shall not appear in any other context. [ Example:

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
};
}
};

end example ]

With that in mind, the following code:

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, "" );
}

passes Daniel Frey's static_assert.

Live example

Building upon the answer by hvd, I found that the only thing that was missing was removing the reference, that is why the std::is_same check fails (b/c the resulting type is actually a reference to the type). Now this parameter-less macro can do all the work. Working example below (I use 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";
}

I don't know all about these wacky templates, how about something super-simple:

#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

Job done, unless you can't stand a couple of macros. You can even use CLASSNAME to declare your constructor(s) (and, of course, destructor).

Live demo.

Finally, a proper solution was found! The idea is by @MitalAshok on github.

#include <type_traits>


namespace SelfType
{
template <typename T>
struct Reader
{
friend auto adl_GetSelfType(Reader<T>);
};


template <typename T, typename U>
struct Writer
{
friend auto adl_GetSelfType(Reader<T>){return U{};}
};


inline void adl_GetSelfType() {}


template <typename T>
using Read = std::remove_pointer_t<decltype(adl_GetSelfType(Reader<T>{}))>;
}


#define DEFINE_SELF \
struct _self_type_tag {}; \
constexpr auto _self_type_helper() -> decltype(::SelfType::Writer<_self_type_tag, decltype(this)>{}, void()) {} \
using Self = ::SelfType::Read<_self_type_tag>;

Then:

struct A
{
DEFINE_SELF
static_assert(std::is_same_v<Self, A>);
};

This uses stateful template metaprogramming to store the type in a context where it's accessible (in the trailing return type of a helper function) and then read the type where it would otherwise be unaccessible (at class scope).

The key here is how the writer is instantiated. Simply doing auto _self_type_helper() -> Writer<...> {return {};} doesn't work: the writer is instantiated with a delay, making the state accessible inside of any member function bodies, but not a the class scope.

But if you do -> decltype(Writer<...>{}, void()), or otherwise make the writer be a part of an expression affecting the type, then it starts working. I'm not entirely sure why that happens.