111

C++ lacks the equivalent of PHP's self keyword, which evaluates to the type of the enclosing class.

It's easy enough to fake it on a per-class basis:

struct Foo
{
   typedef Foo self;
};

but I had to write Foo again. Maybe I'll get this wrong one day and cause a silent bug.

Can I use some combination of decltype and friends to make this work "autonomously"? I tried the following already but this is not valid in that place:

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

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

(I'm not going to worry about the equivalent of static, which does the same but with late binding.)

Community
  • 1
  • 1
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 9
    `this_t` would be probably more aligned with regular C++ naming. – Bartek Banachewicz Jan 15 '14 at 17:13
  • 3
    @BartekBanachewicz: or this_type – PlasmaHH Jan 15 '14 at 17:14
  • Will you accept C++14 answers too? It could work there. – PlasmaHH Jan 15 '14 at 17:16
  • Can you give an example where this would be necessary? What's wrong with `this`? – rwols Jan 15 '14 at 17:21
  • 2
    @rwols: for **type information** at the scope of the class definition, not just the nonstatic implementation? I.e. it'd be nice to be able to write ` public: treenode_type clone() const;` etc. It'd saves you sometimes much work when renaming the classes (and if you cannot 'refactor' it automatically). – quetzalcoatl Jan 15 '14 at 17:24
  • 2
    Even if you did manage to create the `typedef` the way you want it, you'd still have to repeat the class name for constructors. There's no alternative to that. – Praetorian Jan 15 '14 at 17:26
  • 11
    @Praetorian, I can't remember if it was a proposal or not, but someone suggested `auto()` and `~auto()` for ctors/dtors. Interesting to say the least. If used for that purpose, perhaps `typedef auto self;`, but that seems a bit sketchy to me. – chris Jan 15 '14 at 17:27
  • 2
    @Praetorian: But you see a solid, guaranteed compiler error if you get _those_ wrong. – Lightness Races in Orbit Jan 15 '14 at 17:33
  • @rwols: `this` is an expression, not a type, and it does not exist inside `static` member functions. – Lightness Races in Orbit Jan 15 '14 at 17:34
  • 2
    Oh my.. dtors/ctors like `auto() ~auto()` seem sooo great.. edit: at first glance anyways. – quetzalcoatl Jan 15 '14 at 17:40
  • 12
    Honestly, if I was going to suggest syntax to make this possible, it would probably be `decltype(class)`, maybe with a `decltype(struct)` equivalent. That's much clearer than just `auto` in a specific context and I don't see any problems with it fitting into the language based on `decltype(auto)`. – chris Jan 15 '14 at 17:45
  • 1
    +1 for that. Usage like `decltype(class)const&` can start looking odd, but still it would be clear and succint solution. – quetzalcoatl Jan 15 '14 at 17:48
  • 11
    Since you want to avoid errors, you may set up an dummy member function with static_assert, like `void _check() { static_assert(std::is_same::value, "Correct your self type"); }` Doesn't work with class templates, though... – milleniumbug Jan 15 '14 at 17:50
  • @chris: Yeah I like it – Lightness Races in Orbit Jan 15 '14 at 17:51
  • 2
    @chris `class` seems restrictive: `decltype(typename)` is the type of the enclosing type? I guess arrays would be an issue, as you don't know how big they are until they are done. But `decltype(typename)` in the body of an `enum` might be useful. I guess `class` would imply that if you used it in a lambda inside the `class` it would still return the enclosing `class`? Does it work in bodies which are not inline in the `class` definition? – Yakk - Adam Nevraumont Jan 15 '14 at 18:50
  • @Yakk, Interesting points. Either way works for me. I would expect that it would give the type of the lambda functor, yes, and that it would work in more than just the class definition if that doesn't cause unforseen problems. Another handy use might be in definitions: `void decltype(typename)::foo() {}` – chris Jan 15 '14 at 18:55
  • 1
    Code is read far more often than it's written, and classes are read FAR more often than they're renamed. If you *insist* upon having a `self` or `this_t` typedef (and there's absolutely no situation in which you need it), then you should just write it out. Everything suggested here creates a LOT more confusion than clarity. If you want to rename a class, why are you not using find&replace? You don't have to use replace all, just use find next, replace, find next, replace, etc. – Miles Rout Jan 21 '14 at 10:26
  • 3
    @MilesRout: What fun is that?! – Lightness Races in Orbit Jan 21 '14 at 17:20
  • _and cause a silent bug_ Surely you will cause (most likely) a noisy bug. – Paul Sanders Jul 06 '18 at 09:51
  • @PaulSanders: If you're lucky! What you need to be worried about are the silent ones :) Particularly these days when everyone is obsessing over `auto` and/or duck typing (or, at least, simulating it) so there's no longer a point-of-failure diagnostic when you've used the wrong type... you just sort of have to hope that at some point down the line some signatures won't match any more and you finally discover that you made a mistake last year. – Lightness Races in Orbit Jul 07 '18 at 00:20
  • Instead of decltype(typename) - why not just decltype()? – Pete Nov 28 '18 at 10:39
  • @Pete [It doesn't work](http://coliru.stacked-crooked.com/a/d94518f4fa0a7a43), is the main flaw. What is `decltype()`? – Lightness Races in Orbit Nov 28 '18 at 10:40
  • 1
    @LightnessRacesinOrbit looking back over the comments, decltype(class) or decltype(typename) were suggested as possible future syntax. Seems to me that using an empty expression to decltype would be just as clear as decltype(typename) to identify the type of the enclosing scope. And no it is not getting through the compiler today. Wonder if it could be worth making a syntax proposal. – Pete Nov 28 '18 at 12:30
  • @MilesRout, your "self-invalidatingly" hard, dogmatic stance (with those CAPS and _italics_ and "absolutes") misses (among other things) one crucial point entirely (despite explicitly written in the question), which is: reading a class (no matter how many times more than writing) can't put bugs in it. – Sz. Jul 25 '19 at 11:52
  • @Pete Oh I see. Yes that'd be nice actually – Lightness Races in Orbit Jul 25 '19 at 11:55
  • There has been a [proposal](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0874r0.html) to have a syntax for this (and other similar cases), but it’s been sidelined while work on more general reflection work is ongoing. – Davis Herring Aug 22 '19 at 02:59
  • _"Different names have been discussed, including decltype(class), typename(typename) namespace(namespace)"_ Ugh! – Lightness Races in Orbit Aug 22 '19 at 11:13

14 Answers14

41

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.

Ralph Tandetzky
  • 22,780
  • 11
  • 73
  • 120
  • 23
    I guess the answer is "no, but Ralph can!" ;) – Lightness Races in Orbit Jan 16 '14 at 11:47
  • 3
    How is this in any way superior to simply putting the typedef in there? And god, why would you even need the typedef? Why? – Miles Rout Jan 21 '14 at 10:22
  • 7
    @MilesRout This is a question about the question, not the answer. In many cases in software development (and especially maintenance) it is helpful to avoid redundancies in the code, so that changing something in one place does not require you to change code in another place. That's the whole point of `auto` and `decltype` or in this case of `self`. – Ralph Tandetzky Jan 23 '14 at 07:59
  • 2
    `templateclass Self{protected: typedef T self;}; class WITH_SELF(Foo) : public Bar, private Baz {};` would have been simpler and would allow more precise control over inheritance - any reasons against? – Aconcagua Jun 20 '18 at 09:02
  • 1
    @mmmmmmmm, if you haven't learned to deeply appreciate the "Don't Repeat Yourself" principle, chances are you haven't coded quite enough/seriously yet. This "clutter" (far from it, actually) is quite an elegant fix in the context of talking about an inelegant language feature (or misfeature, even deficiency by some strict measures). – Sz. Jul 25 '19 at 12:07
  • How to do private or protected inheritance with this solution?!?! – Isaac Pascual Sep 03 '19 at 09:52
  • Have you another magical Ralph trick for template classes ? :-D – Caduchon Mar 23 '23 at 08:09
39

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.

Sebastian Hoffmann
  • 11,127
  • 7
  • 49
  • 77
  • 4
    No need for `typename` in the typedef. And since this doesn’t reduce the number of redundancies I don’t think it’s a viable alternative. – Konrad Rudolph Jan 15 '14 at 17:16
  • It has exactly the same problem of repeating `Foo` name. – Bartek Banachewicz Jan 15 '14 at 17:17
  • 6
    It _is_ marginally better than the original approach, though, since the repetition is very close together. Not a solution to the question, but +1 for a worthy attempt at a best-case workaround. – Lightness Races in Orbit Jan 15 '14 at 17:18
  • 4
    I used that solution a couple of times, and it has one BAD thing: when later deriving from `Foo`, you have to either: (1) propagate the T upwards to the leaf-descendant, or (2) remember to inherit from SelfT many times, or (3) accept that all children thing to be the Base.. usable, but unpretty. – quetzalcoatl Jan 15 '14 at 17:21
  • @quetzalcoatl: Since I'm trying to replicate `self` rather than `static`, that's no problem. – Lightness Races in Orbit Jan 15 '14 at 17:24
  • Oh, got a typo: `children thing`->`children think`. Sorry, but I didn't understand what you mean now. For me, all (1-2-3) I wrote are quite bad. (1) prevents you from creating nontemplate descendant that uses this feature, (2) sometimes prevents you from inspecting the actual type, and (3) paritally turns off the feature when using inheritance.. if you are aware of it and OK - cool! Also, I don't understand what you mean by `replicating the ` since in C++ `static` is keyword of much different meaning (actually, at least two contextual meanings). Is it in PHP something like `this/self`? – quetzalcoatl Jan 15 '14 at 17:34
  • @quetzalcoatl: Let me clarify: "since I'm trying to replicate PHP's `self` rather than PHP's `static`, that's no problem". In PHP, `static` is like `self` but with late-binding (which solves the problems you just listed). That is, I only need to care about the "current" type and do not need to worry about inheritance. (In the real world we probably do, but I figured I'd get this base case out of the way first before thinking about that.) – Lightness Races in Orbit Jan 15 '14 at 17:50
  • Thank you for clarification. I didn't know there's `static` in PHP. What you say is clear. However, you might misunderstood what I said. If you care about early-bound 'current type' that what I've said holds true. Having `Foo::Self` makes `Foo::self` to be `Foo`. But having then inheritance `Bar : public Foo` preserves that and `Bar::self` is still `Foo`, and using `self` in child class as early-bound type information starts to be problematic since you landed at point (3). You could `Bar : public Foo, public Self` and land at (2), or `Bar : Foo` ~(1). Sorry about nitpicking.. – quetzalcoatl Jan 15 '14 at 18:01
  • Another annoyance is that if `Foo` is a `template`, accessing `self` requires doing `typename Self>::self` which really doesn't help. :) – Yakk - Adam Nevraumont Jan 15 '14 at 19:14
  • And apart from **all** of that, it's still very confusing for anyone reading the code. What on earth is self, and what does it mean? It's a LOT easier to remember to change all instances of the class name in its definition (a single find&replace operation) than to expect everyone that ever inherits from the class to remember to add `self_` to their definitions. – Miles Rout Jan 21 '14 at 10:30
32

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.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 4
    Will this be possibly with C++1y's return type deduction? – dyp Jan 15 '14 at 17:17
  • @dyp: Not in that form since self_() needs to be called on an object – PlasmaHH Jan 15 '14 at 17:17
  • 4
    @dyp For the purpose of my answer that won’t change anything. The error here isn’t in the trailing return type, it’s in the invocation. – Konrad Rudolph Jan 15 '14 at 17:18
  • will `using self = decltype(self_())` compile without instance or Foo:: ? I somewhat doubt. – quetzalcoatl Jan 15 '14 at 17:19
  • 1
    @quetzalcoatl: The innards of `decltype` is an unevaluated context, so invoking the member function isn't the problem (that won't be attempted) – Lightness Races in Orbit Jan 15 '14 at 17:21
  • I'm not going to post this as an answer, but by **declaring** `self_` as `static auto self_() -> decltype(*this);`, it works. Then you should however use std::remove_reference in `using self`. – Tom Knapen Jan 15 '14 at 17:36
  • @LightnessRacesinOrbit: I of course understand that this is unevaluated and just inspected. The problem I meant is: will the compiler at all recognize the identifier `self_` as the member of 'current class' without any scope prefix, be it `variable.` or `namespace::`? If it will pick it (I **very** doubt, but my knowledge of C++ is quite old now), it will work. Else, it will fail with unrecognized symbol. – quetzalcoatl Jan 15 '14 at 17:37
  • 1
    @TomKnapen Try it with clang, and it'll fail. The fact that it's accepted by GCC is a bug, as far as I'm aware. –  Jan 15 '14 at 17:37
  • @TomKnapen: static + this? really? Please include compiler information. I'm pretty sure it is not valid. – quetzalcoatl Jan 15 '14 at 17:38
  • @hvd whoops, you are right. Didn't try clang before commenting. And quetzalcoatl: I know it looks weird, I didn't expect it to work neither, but it works in GCC 4.8.1 – Tom Knapen Jan 15 '14 at 17:40
  • @quetzalcoatl: Oh, okay. Fair question then. – Lightness Races in Orbit Jan 15 '14 at 17:48
  • 4
    FWIW, `struct S { int i; typedef decltype(i) Int; };` works even though `i` is a non-static data member. It works because `decltype` has a special exception where a simple name is not evaluated as an expression. But I cannot think of any way of using this possibility in a way that answers the question. –  Jan 15 '14 at 17:49
  • 1
    Not to mention that it would increase `sizeof(S)` :) – Johannes Schaub - litb Feb 11 '14 at 12:57
  • @JohannesSchaub-litb: Why would adding a `typedef` increase the size of `S`? – Lightness Races in Orbit Apr 02 '14 at 13:57
  • @LightnessRacesinOrbit Not the `typedef`, but the variable definition it requires to work (via `decltype(i)`). – Konrad Rudolph Apr 02 '14 at 14:58
  • @KonradRudolph: Oh, right, I'm with you - it was only introduced in order to obtain its type. Gotcha. It's been a few months since my brain was melted around this subject ;) – Lightness Races in Orbit Apr 02 '14 at 15:08
32

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) {
};
Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135
  • 19
    EWWWW END_CLASS. That's totally unnecessary. – Puppy Jan 15 '14 at 17:18
  • 31
    @DeadMG I think some people might like more consistency; after all, the first macro use doesn't end with `{`, so the `}` is "hanging", which text editors would probably dislike, too. – Bartek Banachewicz Jan 15 '14 at 17:19
  • 7
    Nice idea but even though I’m not fundamentally opposed to macros I would only accept its usage here if it mimicked C++ scoping, i.e. if it were usable as `CLASS_WITH_SELF(foo) { … };` – and I think that’s impossible to achieve. – Konrad Rudolph Jan 15 '14 at 17:20
  • 2
    @KonradRudolph I've added a way to do that, too. Not that I like it, just for the sake of completeness – Bartek Banachewicz Jan 15 '14 at 17:23
  • 1
    There are some problems with that approach, though. First one doesn't allow you to make the class inherit easily (unless you use another macro parameter(s)), and second has all the problems of *inheriting from* it that `Self` has. – Bartek Banachewicz Jan 15 '14 at 17:35
  • To me it looks that inheritance scenarios are the ones where `self` is most useful. – Andrew Savinykh Jan 16 '14 at 01:51
  • You can easily inherit. `WITH_SELF(Foo), public other_parent { ... }` works fine – Yakk - Adam Nevraumont Jan 16 '14 at 15:30
  • @Yakk Not so, if `other_parent` defines a type `self` itself. Then there will be name collisions. Hence, my alternative answer. – Ralph Tandetzky Jan 17 '14 at 08:37
23

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.

  • I think [class.mfct.non-static]/3 says that `_self_fn_1()` is "transformed" into `(*this)._self_fn_1()`. Not sure if that makes it illegal, though. – dyp Jan 18 '14 at 13:12
  • @dyp It says "is used in a member of class `X` in a context where `this` can be used", so I don't think that transformation is performed. –  Jan 18 '14 at 13:17
  • 1
    But then it is neither an implicit nor explicit class member access..? [expr.call]/1 "For a member function call, the postfix expression shall be an implicit or explicit class member access [...]" – dyp Jan 18 '14 at 13:19
  • (I mean, what happens when you have `auto _self_fn_1() -> decltype(*this); auto _self_fn_1() const -> decltype(*this);`?) – dyp Jan 18 '14 at 13:21
  • @dyp [expr.call]/1 is a good point, I'll have to take a closer look. About `const` overloads, though: that isn't a problem. 5.1p3 has specifically been modified to also apply to static member functions, and says the type of `this` is `Foo*`/`Bar*` (without `const`), because there is no `const` in the declaration of `_self_fn_2`. –  Jan 18 '14 at 13:25
  • @dyp I fear you're right about [expr.call]p1. It may avoid the restrictions on `this`, but that just makes it invalid for another reason. –  Jan 18 '14 at 13:50
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
};

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.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • That's not bad either! – Lightness Races in Orbit Jan 15 '14 at 19:15
  • @LightnessRacesinOrbit now with `template` class support options. – Yakk - Adam Nevraumont Jan 15 '14 at 23:12
  • I was thinking exactly about this as I was leaving work yesterday. You beat me to it :). I'd suggest declaring self_check() as inline, to avoid linking issues (same symbol Foo::self_check() found in multiple object files). – the swine Jan 16 '14 at 10:57
  • @LightnessRacesinOrbit Uh ... what? I didn't understand a word you said. What's 9.3/2? Pardon my ignorance ... – the swine Jan 16 '14 at 12:54
  • 1
    @theswine: 9.3/2 is the index of a paragraph in the C++ standard, which guarantees that class member functions _defined in the body of the class definition_ are already, implicitly, `inline`. That means you don't need to write `inline` at all. So if you have been writing `inline` in front of every such class member function definition for your whole career, you can stop now ;) – Lightness Races in Orbit Jan 16 '14 at 12:59
  • 2
    @LightnessRacesinOrbit Oh, actually I was. Thank you, that will save me some typing in the future :). I'm always amazed by how much I don't know about C++. – the swine Jan 16 '14 at 13:10
  • So the `static` `T*` part above does nothing: but the the line check does help. Need to add in a file name hash to replace the `T*` it... or maybe a literal `constexpr`? – Yakk - Adam Nevraumont Jan 16 '14 at 13:55
  • With a less nice error message, I think `auto self_check() -> enable_if_t< std::is_same::value >;` should work as well. [You can also use the same technique and produce a nicer error message](http://coliru.stacked-crooked.com/a/f514a8e294255578). – dyp Jan 18 '14 at 12:52
  • Not bad? Not bad? It's absolutely awful. Just like everything on this page, it makes major clarity sacrifices for absolutely no gain. – Miles Rout Jan 21 '14 at 10:31
11

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 :(

Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • And the same problem happens with a regular `int T::*` pointer to member variable. And `int self_var; typedef decltype(&self_var) self_ptr` doesn't work either, that's just a regular `int*`. – MSalters Jan 16 '14 at 13:55
9

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

Community
  • 1
  • 1
5

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.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • 2
    Great! Actually one can omit both `, void()` and the member function body, demo: https://gcc.godbolt.org/z/rrb1jdTrK – Fedor Jan 13 '22 at 21:01
4

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)
TAS
  • 2,039
  • 12
  • 17
3

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";
}
niksbenik
  • 39
  • 2
1

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); }
};
user1899020
  • 13,167
  • 21
  • 79
  • 154
0

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.

the swine
  • 10,713
  • 7
  • 58
  • 100
  • You’re essentially re-implementing `static_assert` here, aren’t you? Furthermore, your complete code is invalid because you’re using illegal (reserved) identifiers. – Konrad Rudolph Jan 16 '14 at 13:43
  • @KonradRudolph Yes, that is indeed the case. I don't have C++0x at the time, so I reimplemented static_assert in order to provide complete answer. I say that in the answer. Is it invalid? Can you point out how? It compiled fine, I'm using it right now. – the swine Jan 16 '14 at 14:27
  • 1
    The identifiers are invalid because C++ reserves everything with a leading underscore followed by uppercase letter, as well as two leading underscores in global scope, for the compiler. User code must not use it, but not all compilers will flag it as an error. – Konrad Rudolph Jan 16 '14 at 15:48
  • @KonradRudolph I see, I did not know that. I have a lot of code that use that, never had problems with it on neither Linux / Mac / Windows. But I guess it is good to know. – the swine Jan 16 '14 at 15:57
0

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.

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48