2

I want to make a function publicly available in a derived class, only when the calling parameter has a special tag. I'm using C++14 with Clang.

Giveǹ is the following situation:

struct TagA {};
struct TagB {};

struct Base {

protected:
    void handler(const std::shared_ptr<Base> &obj) {
        ...
    }
};

The goal is to make handler available in derived classes, if they have a special tag, to allow different "classes" of derived objects. Like this:

struct DerivedA : Base, TagA {
    // This should only be available, if the obj argument for handler 
    // derives from TagA
    // something like std::enable_if<std::is_base_of<TagA, ???>::value>
    using Base::handler;
};

This would allow DerivedA to handle

struct HandledA : Base, TagA {
};

but not

struct UnhandledA : Base, TagB {
};

although UnhandledA inherits from Base and handler only needs Base.

Mike M
  • 2,263
  • 3
  • 17
  • 31
  • 1
    overload the function making it public with that parameter – ΦXocę 웃 Пepeúpa ツ Apr 30 '17 at 08:54
  • @ΦXocę웃Пepeúpaツ How would the TagA come into play with an overloaded function? – Mike M Apr 30 '17 at 09:00
  • What ownership scenario requires passing a `const std::shared_ptr &` ... ? – Daniel Jour Apr 30 '17 at 11:15
  • @DanielJour I pass it generally by ``const &``. See http://stackoverflow.com/a/8741626/2524462 for the reasoning. – Mike M Apr 30 '17 at 17:23
  • @MikeM yes yes, I know about that. What I intended to question is why there even is an ownership transfer. If the structure is "aggregate/composite/tree" like then unique ownership would be better suited, if the relationship between the objects here is similar to the nodes in a graph then shared ownership quickly creates cycles, which could be avoided by giving an external object control over the lifetime of the objects. – Daniel Jour Apr 30 '17 at 18:21
  • @DanielJour Currently I'm working with a tree structure, so ownership cycles are no problem. Additionally I'm just prototyping something, so it may need cleanup if it goes into production. – Mike M May 01 '17 at 09:29

2 Answers2

3

If I understud your question correctly you can achieve this using CRTP (working version):

#include <memory>
#include <type_traits>

struct TagA {};
struct TagB {};

template< typename tp_Derived > struct BaseCRTP;

struct Base
{
    template< typename > friend
    struct BaseCRTP;

    private: void
    handler_impl(const std::shared_ptr<Base> &) {}
};

template< typename tp_Derived >
struct BaseCRTP: public Base
{
    public: void
    handler(const std::shared_ptr<Base> & obj)
    {
        static_assert
        (
            ::std::is_base_of< TagA, tp_Derived >::value
        ,   "\"handler\" method can only be used in classes deriving from TagA"
        );
        return(handler_impl(obj));
    }
};

struct DerivedA : BaseCRTP< DerivedA >, TagA
{};

struct DerivedB : BaseCRTP< DerivedB >, TagB
{};

int main()
{
    DerivedA a; // OK
    (void) a; // not used
    auto pha(&DerivedA::handler); // OK
    DerivedB b; // OK
    (void) b; // not used
    auto phb(&DerivedB::handler); // static_assertion failure
    return 0;
}
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
user7860670
  • 35,849
  • 4
  • 58
  • 84
2

Another CRTP solution could involve SFINAE (std::enable_if<>) as follows

#include <memory>
#include <type_traits>

struct TagA {};
struct TagB {};

template <typename Der>
struct Base
 {
   protected:
      template <typename D = Der>
      typename std::enable_if<std::is_base_of<TagA, D>::value>::type
            handler (std::shared_ptr<Base> const &)
       { }
 };

struct DerivedA : Base<DerivedA>, TagA
 { using Base::handler; };

struct DerivedB : Base<DerivedB>, TagB
 { using Base::handler; };

int main ()
 {
   DerivedA{}.handler(nullptr); // compile
   DerivedB{}.handler(nullptr); // compilation error
 }

To avoid that the use of handler could be anbled expliciting the template type as follows

DerivedB{}.handler<TagA>(nullptr); // now compile

you can improve the std::enable_if test imposing also that D is equal to Der

  template <typename D = Der>
  typename std::enable_if<   std::is_base_of<TagA, D>::value
                          && std::is_same<D, Der>::value>::type
        handler (std::shared_ptr<Base> const &)
   { }
max66
  • 65,235
  • 10
  • 71
  • 111