Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template meta-code and private members

I would like to do something like this:

template <typename T>
class Foo
{
...
public:
    void DoSomething()
    {
        compile_time_if (T is ClassA)
        {
            m_T.DoThingOne();
            m_T.DoThingTwo();
        }
        DoSomeFooPrivateThing();
        m_T.DoThingThree();
    }
    T m_T;
};

In this case I know that all valid T implement DoThingThree, but only ClassA implements DoThingOne and DoThingTwo. This is not a duck-typing thing, I do want to only do this extra part for ClassA and I do not want to add these methods to the other possible Ts. I can't do casting, because the possible Ts are not inherited types.

I know that I can use an external helper template to accommodate this:

template <typename T>
void Foo_DoSomething(T& t)
{
    t.DoThingThree();
}

template <>
void Foo_DoSomething(ClassA& t)
{
    t.DoThingOne();
    t.DoThingTwo();
    t.DoThingThree();
}

template <typename T>
class Foo
{
...
public:
    void DoSomething()
    {
        Foo_DoSomething(m_T);
    }
...
};

However now this external template doesn't have access to private members of Foo (can't call DoSomeFooPrivateThing), which limits its functionality, and it's exposed publicly to the outside, which isn't pretty. (Making the external method a friend just makes things worse.)

Another seemingly-reasonable option is to implement it internally:

template <typename T>
class Foo
{
...
public:
    void DoSomething()
    {
        DoSomethingImpl(m_T);
    }
...
private:
    template <typename T2>
    void DoSomethingImpl(T2& t)
    {
        DoSomeFooPrivateThing();
        t.DoThingThree();
    }

    template <>
    void DoSomethingImpl(ClassA& t)
    {
        t.DoThingOne();
        t.DoThingTwo();
        DoSomeFooPrivateThing();
        t.DoThingThree();
    }
...
};

But this requires duplicating the outer template type and parameter. This is probably acceptable, but it still feels a bit odd. Sadly it doesn't actually compile (at least not in GCC, as it objects to specialisations inside classes).

Is there a better way to do this?

like image 454
Miral Avatar asked Mar 27 '15 05:03

Miral


2 Answers

I think your last option is the best one.

Instead of

template <>
void DoSomethingImpl(ClassA& t)
{
    t.DoThingOne();
    t.DoThingTwo();
    DoSomeFooPrivateThing();
    t.DoThingThree();
}

you can just use (no need for use of template here):

void DoSomethingImpl(ClassA& t)
{
    t.DoThingOne();
    t.DoThingTwo();
    DoSomeFooPrivateThing();
    t.DoThingThree();
}
like image 200
R Sahu Avatar answered Sep 28 '22 23:09

R Sahu


First solution: As you say,we could do like this:

template <typename T> class Foo{

    public:
    void doSomething(){
        doSomething(std::is_same<T,A>());
    }
    private:
    void doSomething(std::true_type){
        cout<<"A do"<<endl;
    }
    void doSomething(std::false_type){
        cout<<"any other do"<<endl;
    }
};

Second solution: Because template class Foo only has one template parameter,so we can directly make a explicit specialization like this.

template <typename T> class Foo{

    public:
     void doSomething(){
         cout<<"any other do..."<<endl;
     }

};

template<> void Foo<A>::doSomething(){
    cout<<"A do"<<endl;

}

Thrid solution: Maybe not a good way, we could do like this, This way(SFINAE) use C++11 or boost enable_if.When the type is same with A,compiler auto select expected class.

#include <iostream>
using namespace std;
class A {};

template <typename T,typename Enable = void>
class Foo
{
public:

    void DoSomething()
    {
        cout<<"anyother do"<<endl;
    }
private:
    T m_T;
};
template <typename T> class Foo<T, typename enable_if<is_same<T,A>::value>::type >
{
public:
    void DoSomething()
    {
        cout<<"A do"<<endl;
    }
private:
    T m_T;
};

More:

If two Foo have many same things,we can make base class for it like this:

template <typename T> class BaseFoo{
 ...
};

and two template class derived from BaseFoo like this:

    template <typename T,typename Enable = void> 
class Foo:public BaseFoo<T>{...}


    template <typename T> class Foo<T, typename enable_if
<is_same<T,A>::value>::type >:public BaseFoo<T>{...}
like image 38
Ron Tang Avatar answered Sep 28 '22 23:09

Ron Tang