0

I've written what I would hoped to be a general purpose CRTP class that allows my own classes to easily extend a class that implements a shared_from_this function (presumably returning a shared pointer equivalent to this), giving direct access to the shared pointer for the derived class instead of the base, as follows:

template<class Self, class Super>
class inherit_shared_from_this : public Super {
public:
    std::shared_ptr<const Self> shared_from_this() const {
        return std::static_pointer_cast<const Self>(Super::shared_from_this());
    }
    std::shared_ptr<Self> shared_from_this() {
        return std::static_pointer_cast<Self>(Super::shared_from_this());
    }
};

This works quite well for single inheritance trees, but I was wanting to extend it to be able to inherit from more than one class that implements shared_from_this. When I do so, however, I get an error 'shared_from_this' is ambiguous, because the compiler can't tell which exact class I am meaning to inherit the shared_from_this function. In fact, it does not matter which one.

What I wanted to do is define a class as follows:

class Foo : public inherit_shared_from_this<Foo, A, B, C> {
...
};

Where all of A, B, C, and D implement a shared_from_this function, and this class will publicly inherit from each of the classes other than the first, and implement exactly two shared_from_this methods, for both const and non-const versions, to return the applicable shared_pointer version of this (in this case, std::shared_ptr<Foo>).

The first thing I tried to do was adding a variadic definition for the class as follows:

template<class Self, class Super, typename... Args>
class inherit_shared_from_this : public Super, public inherit_shared_from_this<Self, Args...> {
public:
    std::shared_ptr<const Self> shared_from_this() const {
        return std::static_pointer_cast<const Self>(Super::shared_from_this());
    }
    std::shared_ptr<Self> shared_from_this() {
        return std::static_pointer_cast<Self>(Super::shared_from_this());
    }
};

That did not work, however... it objected that the two definitions of inherit_shared_from_this had different numbers of parameters.

Any ideas on how I can do what I am trying to accomplish, or is it just not possible?

EDIT:

A clarification on why std::shared_from_this is not usable as-is:

If I simply inherit directly from the class that inherited std::enable_shared_from_this in a derived class that also inherits from something that inherited from std::enable_shared_from_this, then it returns the wrong pointer type. If I use multiple inheritance and also inherit from std::enable_shared_from_this in a derived class whose base had extended std::enable_shared_from_this, then calling shared_from_this() in the derived class is ambiguous. Therefore a class that can inherit from such classes is required if access to the derived pointer is wanted.

EDIT: This question differs from mine in that I am wanting to avoid adding virtual inheritance into things unless it is explicitly asked for. Since std::enable_shared_from_this itself does not have a virtual destructor in the first place, it seems improper to add that cost to what I am trying to design as a general purpose class whose purpose is to be just as reusable.

markt1964
  • 2,638
  • 2
  • 22
  • 54
  • Why are you not using [`std::enable_shared_from_this`](http://en.cppreference.com/w/cpp/memory/enable_shared_from_this)? – Walter Mar 15 '18 at 23:37
  • I do use it. The problem is with descendant classes... I had assumed that this was obvious, and so did not mention it previously. I've updated the text of the problem. – markt1964 Mar 15 '18 at 23:41
  • I think I begin to understand what your issue is. The problem with the different numbers of parameters can be solved by specialisation: the version with the smaller number of parameters can be specialised version. Even better, the variadic version alone should suffice, as an empty pack is allowed. – Walter Mar 15 '18 at 23:43
  • why does your variadic `inherit_shared_from_this` inherit from `inherit_shared_from_this`? – Walter Mar 15 '18 at 23:52
  • Well, it's already inherited directly from Super, so it doesn't need that anymore... so it needs to inherit publicly from the next Super in the Args list – markt1964 Mar 15 '18 at 23:53
  • Isn't this a duplicate of [Use of enable_shared_from_this with multiple inheritance](https://stackoverflow.com/questions/16082785/use-of-enable-shared-from-this-with-multiple-inheritance)? Also for single inheritance, you don't need anything else than `std::enable_shared_from_this`, don't you? – Walter Mar 15 '18 at 23:59
  • For single inheritance, I can use std::enable_shared_from_this for each of my base types, and then my initial implementation of inherit_shared_from_this for any derived types. That works just fine for that case. I was wanting to extend it, however, to accomodate multiple inheritance. – markt1964 Mar 16 '18 at 00:13
  • If a derived class inherits two different base classes that inherit specializations of `std::enable_shared_from_this`, I think you have bigger problems. Doesn't the compiler complain when you try to create a `shared_ptr` to such a type? – aschepler Mar 16 '18 at 00:34
  • Yes... that's why I was wanting to use a CRTP to implement its own `shared_from_this` to override the other base class's `shared_from_this` methods. – markt1964 Mar 16 '18 at 00:36
  • Oh, hmm. I guess that doesn't actually cause compile errors like I expected, at least with the g++ library. Instead, `shared_from_this` simply fails: http://coliru.stacked-crooked.com/a/1770d06dcc62889e – aschepler Mar 16 '18 at 00:43
  • Aha, the behavior of my little program is correct. Per the Standard, the functions such as `make_shared` or `shared_ptr` constructors which enable `shared_from_this` only work if the type has an **unambiguous** and accessible base class which is a specialization of `enable_shared_from_this`. Otherwise, just nothing happens. – aschepler Mar 16 '18 at 01:00

0 Answers0