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
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();
}
...
};
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.