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, I only want to do this extra part for ClassA , and I don't want to add these methods to other possible T s. I cannot do casting because possible T are not inherited types.

I know that for this you can use an external helper template:

 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 does not have access to private members of Foo (cannot call DoSomeFooPrivateThing ), which limits its functionality, and publicly goes outside, which is not so. (Making an external method a friend just complicates the situation.)

Another seemingly reasonable option is its internal implementation:

 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 duplication of the external type of the template and parameter. This is probably acceptable, but still seems a bit odd. Unfortunately, it doesnโ€™t actually compile (at least not in GCC, since it jeopardizes specialization within classes).

Is there a better way to do this?

+7
c ++ c ++ 11 templates c ++ 03
source share
3 answers

I think your last option is the best.

Instead

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

you can just use (no need to use template here):

 void DoSomethingImpl(ClassA& t) { t.DoThingOne(); t.DoThingTwo(); DoSomeFooPrivateThing(); t.DoThingThree(); } 
+3
source share

First decision: 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: Since there is only one template parameter in the Foo template class, we can directly do this explicit specialization.

 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 this is not a good way, we could do it this way (SFINAE) uses C ++ 11 or boost enable_if. When the type is the same as A, the compiler automatically selects the 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 details:

If two Foo have many identical things, we can make a base class for it as follows:

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

and two classes of patterns derived from BaseFoo as follows:

  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>{...} 
+1
source share

I'm used to your last decision, so it doesn't seem strange to me.
If you prefer, you can always do

  void DoSomethingImpl(T&t, std::true_type){...} 

and

  void DoSomethingImpl(T&t, std::false_type){...} 

and then

  DoSomethingImpl(m_T, std::is_same<T, ClassA>{}); 

This is functionally equivalent, but also allows more complex decision-making if functionality needs to be expanded.

0
source share

All Articles