Can I implement an autonomous `self` member type in C++?
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.
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 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.
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 classT
, or a derived class ofT
, then the implied object argument is(*this)
. If the keywordthis
is not in scope or refers to another class, then a contrived object of typeT
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 ofT
, 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.