1

I am kind of C++ newbie, especially when dealing with templates. I have a template class "Foo" that is intended to take different structures as template parameters. I need also to have a member function of the class that works differently depending on the type template parameter, so I specialise such a function. The general picture would be as follows

struct A
{

    float paramOfA;

};

struct B
{

    float paramOfB; 

};


template <typename T> 
class Foo
{
    public:
        void doSomethingOnType(T& arg);

    //...more functions and stuff...

};

// function specialisation for A's
template<> void Foo<A>::doSomethingOnType(A& a){

    //do something on member of A
    a.paramOfA = ...;

    std::cout<< "I've done something on a's member..."<<std::endl;


}

// function specialisation for B's
template<> void Foo<B>::doSomethingOnType(B& b){
    //do something on member of B
    b.paramOfB = ...;

    std::cout<< "I've done something on b's member..."<<std::endl;

}

So far so good, right? Imagine that now I have a structure C that derives from B:

struct C:B
{

    float paramOfC;

};

Now, when I instantiate a Foo object that takes C structure template type, I would want the function "doSomethingOnType" to keep the same behaviour of the function for B types on C's member that derives from B (paramOfB), eventhough I haven't specialised such a function for C structure types. For instance

Foo<C> o;
C oneC;

o.doSomethingOnType(oneC);

I am sure that when executing the above piece of code, the function will take any implementation given in the templated class, not in the specialised version for B. But I really want to keep the latter implementation of the function when using C types since, being C derived from B, it would make a lot of sense to me as well as saving me time from having to write more lines of code for a function specialisation for C's that has the same behaviour than for B (imagine that B has 50 members instead of a single one). Is it possible to do so without, as I said, specialising the function for C structure types?

Thanks a lot in advance for any help!

Really excited to ask me first question in stackoverflow :-)

Javi RD
  • 13
  • 2
  • This one may sorta answer your question: http://stackoverflow.com/questions/14612410/how-do-you-force-a-templatization-to-match-a-base-class (basically: specialize for Derived doing only an explicit call to Base's class version, e.g.: `doSomething(C& arg) { doSomething(arg); }`. But this is a workaround rather than a solution (imagine you had lots of classes...). – hauron Dec 26 '15 at 11:46
  • @hauron Thanks a lot for the response! What you proposed makes a lot of sense as well as being very simple to implement. I will keep it in mind while looking for others since, as you said, it looks more a workaround rather than a solution, a workaround that maybe it's the only way to do what I want. – Javi RD Dec 26 '15 at 12:02
  • Keep in mind it may be better to drop the template altogether in this case, since what you want is polymorphism instead. – hauron Dec 26 '15 at 12:06
  • Yeap, I was aware of that but, unfortunately I can't drop the template embedding :-( – Javi RD Dec 26 '15 at 12:15
  • @LogicStuff Thanks a lot! It is really usefeul, but not quite for the same question as mine. – Javi RD Dec 26 '15 at 12:37

2 Answers2

0

Although How do you force a templatization to match a base class? is an elegant solution if your function takes an instance of T as a parameter, I would like to introduce several ways if it does not:

TestCase

class A1 {};
class A2:public A1 {};

class B{};
class C{};

Wrapped Function

This might be the simplest solution:

template<typename T>
class Bar
{
        public:
        void fun()
        {
                fun_impl((T*)(0));
        }
        void fun_impl( A1* const)
        {
                cout << "A1" << endl;
        }
        void fun_impl(B* const)
        {
                cout << "B" << endl;
        }
        void fun_impl(void*)
        {
                cout << "Neither A nor B" << endl;
        }
};

Bar<A2>().fun(); // A1
Bar<B>().fun();  // B
Bar<C>().fun();  // Neither A nor B

Because the precedence of fun_impl which exactly matches(or as an accessible base class of) the type > those which requires void* conversion, the correct version will be enabled. (NOTE: This is true on clang3.7 and gcc5.3, but I din't refer to standard)

However, if we haveclass A3: private A1, an error will be raised during the compilation of Bar<A3>().fun().

The following two solutions require C++11:

Partial Specialization of Class Template

template<typename T, bool  = std::is_base_of<A1, T>::value,

bool = std::is_base_of::value > struct Foo { void fun(); }; //Main Version

template<typename T>
struct Foo<T,false,false>
{
        void fun();
};  //Specialization of Neither A nor B

template<typename T>
void Foo<T,false,false>::fun()
{
        cout << "neither A nor B" << endl;
}

template<typename T>
struct Foo<T,true,false>
{
        void fun();
};  //Specialization of A

template<typename T>
void Foo<T,true,false>::fun()
{
        cout << "A" << endl;
}

template<typename T>
struct Foo<T, false, true>
{
        void fun();
};  //Specialization of B

template<typename T>
void Foo<T,false,true>::fun()
{
        cout << "B" << endl;
}

    Foo<A2>().fun();  //A
    Foo<B>().fun();   //B
    Foo<C>().fun();   //Neither A nor B
    Foo<APrivate>().fun();    //A

SFINAE

If you don't want to specialize the whole class, maybe SFINAE in one class could be the best choice:

namespace
{
        class Helper1 {};
        class Helper2 {};
} // Helper classes to avoid ambiguity

template<typename T>
class Foo
{
        public:

        template<typename TIn= T, typename U= typename std::enable_if<std::is_base_of<A1, TIn>::value>::type >
                void fun(Helper1 = Helper1())
                {
                        cout << "A" << endl;
                }
        template<typename TIn=T ,typename U = typename std::enable_if<std::is_base_of<B, TIn>::value>::type >
                void fun(Helper2 = Helper2())
                {
                        cout << "B" << endl;
                }
        template<typename TIn = T, typename = typename std::enable_if<!std::is_base_of<A1,TIn>::value>::type ,
                typename = typename std::enable_if<!std::is_base_of<B,TIn>::value>::type >
                void fun()
                {
                        cout << "Neither A nor B" << endl;
                }
};

In the case above a function will be instantiated only if it matches some certain conditions. Since three void fun() are not allowed in one class, helper classes are required.

Community
  • 1
  • 1
lz96
  • 2,816
  • 2
  • 28
  • 46
  • @Iz96 thanks a lot for the solutions. I already used another one provided by another user that has responded to my question too. I will have in mind your suggestions for the future, specially that one about SFINAE which I was totally unawared of – Javi RD Jan 07 '16 at 09:53
0

You can do some hook thing in general template function, though I hate it.... these code can as following:

template <typename T>
class Foo {
public:
  void doSomethingOnType(T& arg)
  {
    if (dynamic_cast<B*>(&arg) != NULL) {
      Foo<B> fb;
      fb.doSomethingOnType(arg);
    } else {
      std::cout << "I've done something on Foo's member..." << std::endl;
    }
  }

};

Roland Yim
  • 36
  • 6