1

I have a member function of a class in which I'd like to use perfect forwarding for one of the parameters. However, the function being forwarded to only accepts a single argument of type c2Type, so I'd like the calling function also to only accept c2Type arguments, but obviously have to keep the universal reference to do the forwarding. It seems it can be accomplished using a default template parameter like so:

class c2Type 
{
    // some data members...
};

template<typename T, typename isC2Type = typename std::enable_if<
               std::is_same<c2Type, typename std::decay<T>::type>::value>::type>
    void configurationMessageHandler(T&& message)
{
    // some stuff...
   mapAddress(std::forward<c2Type>(message));   
}

mapAddress(c2Type&& message)
{
    // do stuff...
};  

However, I need to check for this type on several member functions, and also such a long template seems unfriendly and unreadable. What I'd like to is create an alias for isC2Type like

template<typename T>
using isC2Type = typename std::enable_if<
           std::is_same<c2Type, typename std::decay<T>::type>::value>::type;

which I thought would make the configurationMessageHandler template look like

template<typename T, isC2Type<T>>

but that doesn't compile. How can I properly use aliases in this case?

Nicolas Holthaus
  • 7,763
  • 4
  • 42
  • 97

1 Answers1

2

I hope this can help you.

template<class U, class T, 
         class= std::enable_if_t<std::is_same<std::decay_t<T>, U>::value, T>>
using LimitTo = T;

template<class T>
void configurationMessageHandler(LimitTo<c2Type,T>&& message){
    // some stuff...
    mapAddress(std::forward<T>(message)); 
    //!! Use T because the reference of c2Type maybe has cv-qualify
}

Even when there are many parameters, for example:

void foo(int); 
template<class A, class B, class C>
void foo(LimitTo<int,A>&& , LimitTo<float,B>&& , LimitTo<bool,C>&& );
template<class T>
void foo(LimitTo<string,T>&& ); 

However, there are some pitfalls with this trick:

  1. Be careful that in some cases it won't work. This leads to fatal errors in some compilers. I don't know why.

      template<class...Args> 
      void foo(LimitTo<double,Args>&&... args){}
    
  2. Use default parameters to avoid ambiguities, e.g. with regards to such constructors:

      template<class T>
      Ctor(LimitTo<string,T>&&,string* =nullptr) {} //string* or anything else
    
      template<class T>
      Ctor(LimitTo<double,T>&&, double* =nullptr) {}
    

Inherited constructors will cause some problems as default parameters can't be inherited. So change it to:

 template<class T>
 Ctor(LimitTo<string,T>&&,string*) {} 
 template<class T>
 Ctor(LimitTo<double,T>&&, double*) {}
Landersing
  • 66
  • 5
  • This is a very clever idea. I think calling `Tself` something like `LimitTo` or `UniversalRef` with the order of the template parameters reversed would make it more readable. – Nicolas Holthaus Feb 07 '16 at 13:42
  • @NicolasHolthaus yes,LimitTo or UniversalRef is also a good name.I thought that Tself is means T itself. – Landersing Feb 07 '16 at 14:18
  • @NicolasHolthaus I found that there is a potential error ,and we should use forward T instead of forwarding the real type – Landersing Feb 08 '16 at 01:24