简体   繁体   中英

Make template function to destinguish inheritors and others

I have a class ( base ) which has many inheritors ( derived_i ) and some other types ( other ). I want to have a template method in some class-handler ( caller ), which handles inheritors of base in differ way, then from other types.

Here is an example code I telling about.

#include <iostream>

using namespace std;

template <typename T>
class base {
public:
    base (T val = T())
        : m_val(val)
    {}
    base (const base &other)
        : base(other.m_val)
    {}
    virtual void base_specific_method()
    {
        cout << __func__ << " method called: " << m_val << endl;
    }
    void each_class_has_this() {
        cout << __func__ << " this is boring..." << endl;
    }
    T m_val;
};
class other {
public:
    void each_class_has_this() {
        cout << __func__ <<" this is boring..." << endl;
    }
};
class derived_i : public base <int>
{
public:
    derived_i () : base <int> (10)
    {}
    virtual void base_specific_method()
    {
        cout << __func__ <<" Hey! I'm interesting derived! And 10 == " << m_val << endl;
    }
};

template <typename T>
class caller {
public:
    caller (T val = T())
        : m_val(val)
    {}
    void call() {
        p_call(m_val);
    }
private:
    template <typename T1> void p_call (T1 &val)
    {
        val.each_class_has_this();
    }
    template <typename T1> void p_call (base<T1> &val)
    {
        val.base_specific_method();
    }
private:
    T m_val;
};

int main ()
{
    caller<other> c1;
    caller<base<double> > c2;
    caller<derived_i > c3;

    c1.call();
    c2.call();
    c3.call();
}

It compiled with g++ -std=c++11 test.cpp and output is next:

  each_class_has_this this is boring...
  base_specific_method method called: 0
  each_class_has_this this is boring...

While I'm expecting

  each_class_has_this this is boring...
  base_specific_method method called: 0
  base_specific_method Hey! I'm interesting derived! And 10 == 10

Is there any way to change this code to make it suitable my requests?

This question seems to be a duplicate of another question , but the correct answer on it leads to the problem, I faced here.

PS There is no way to make base and other the inheritors from one class. =(

You can get the desired behaviour using SFINAE:

Add a class has_specific like this:

template <typename T>
class has_specific
{
    typedef char one;
    typedef long two;

    template <typename C> static one test( typeof(&C::base_specific_method) ) ;
    template <typename C> static two test(...);    

public:
    enum { value = sizeof(test<T>(0)) == sizeof(char) };
};

And change your definition of p_call to this:

template <typename T1=T>
typename enable_if<!has_specific<T1>::value,void>::type
p_call (T1 &val)
{
    val.each_class_has_this();
}

template <typename T1=T>
typename enable_if<has_specific<T1>::value,void>::type
p_call (T1 &val)
{
    val.base_specific_method();
}

In this case i used the SFINAE in the return type. It should be possible to use it in the template arguments list or in the parameters list as well, but this one was the one i got working first.
As a technical detail, this implementation does not depend on if you have a class derived from base , but if a method base_specific_method() is present, but i hope it still helps with your problem.

Try it online
has_specific taken from here
return type SFINAE taken from here

You can use std::enable_if with std::is_base_of to distinguish the class derived from base<T> or not.

template <typename T, typename TT = void>
class caller {
public:
    caller (T val = T())
        : m_val(val)
    {}
    void call() {
        p_call(m_val);
    }
private:
    template <typename T1> typename std::enable_if<!std::is_base_of<base<TT>, T1>::value>::type p_call (T1 &val)
    {
        val.each_class_has_this();
    }
    template <typename T1> typename std::enable_if<std::is_base_of<base<TT>, T1>::value>::type p_call(T1& val)
    {
        val.base_specific_method();
    }

private:
    T m_val;
};

Note that base is a template class, which means T1 might be derived class of base<int> , or base<double> , and so on. We can't count all the possibilities, so the template parameter TT need to be specified as the hint. Then use it as:

caller<other> c1;
caller<base<double>, double> c2;
caller<derived_i, int> c3;  // derived_i derived from base<int>

c1.call();
c2.call();
c3.call();

Result:

each_class_has_this this is boring...
base_specific_method method called: 0
base_specific_method Hey! I'm interesting derived! And 10 == 10

LIVE

Or you can make a most base class to make it simple:

class abstract_base {
public:
    virtual void base_specific_method() = 0;
    virtual ~abstract_base() {}
};
template <typename T>
class base : public abstract_base {
    ...
};
...
template <typename T>
class caller {
    ...
    template <typename T1> typename std::enable_if<!std::is_base_of<abstract_base, T1>::value>::type p_call (T1 &val)
    {
        val.each_class_has_this();
    }
    template <typename T1> typename std::enable_if<std::is_base_of<abstract_base, T1>::value>::type p_call(T1& val)
    {
        val.base_specific_method();
    }
    ...
};

LIVE2

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM