Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 : How can I define a function that accept a universal reference of a specific type of object?

Tags:

c++

c++11

Problem: I am developing a program with C++11. I want to write a function that accept both rvalue reference and lvalue reference. (i.e. universal reference).

The following function accept a universal reference parameter:

template<class T> void function(T&& t){/*SNIP*/}

However, it accepts all type of parameter. It ruins the type safety of the function. What should I do if I want it to accept a specific type of parameter?

Here is a solution that I can think of:

void function(Class& t){/*SNIP*/}
void function(Class&& t){ function(t); }

However, it's ugly. In case I want to change the parameter to be accepted, or changing the function name, I have to update both version of the function. It there a better equivalent than this?

EDIT: Problem solved. You both have answered quite well. I have voted +1 to both answers to show my appreciation. I am going to leave this question for a few days. The answer with the most votes will be accepted.

EDIT2: I end up with the following code:

template < class T,
class=typename std::enable_if<std::is_same<Class, typename std::decay<T>::type>::value>::type //Dummy template parameter
>
void function(T&&){}

EDIT3: I have written a macro definition for this purpose:

#define uRefType(T, typeLimit) class T, class=typename std::enable_if<std::is_same<typename std::decay<T>::type, typeLimit>::value>::type

Usage example:

template< uRefType(T, Class) > void function(T&&){}
like image 650
Konfle Dolex Avatar asked Feb 17 '13 07:02

Konfle Dolex


2 Answers

One way of doing this is to use std::enable_if. That's a struct provided by the type_traits header. It is defined in such a way that enable_if<A,B>::type is the type B if the boolean condition A evaluates to true at compile time. Otherwise it is empty.

Hence, if you have a function template

template <typename T>
void fun(T &&)
{ /*...*/ }

and you want to ensure it is only defined if T is a certain type, you can use the enable_if<...>::type construct instead of the return type (here, void). The boolean condition A is then defined as something like: T is int, and the type B is defined as the original return type of the function (here, void).

So, if we want fun to be defined only if T is int, we get this:

#include <type_traits>

template <typename T>
typename std::enable_if<std::is_same<int,typename std::decay<T>::type>::value,void>::type
fun(T &&)
{ }

int main()
{
  int    lvali = 3;
  double lvald = 3.3;

  fun(3);
  fun(lvali);

  // fun(3.3);      // this won't be accepted (not an int)
  // fun(lvald)     // this won't be accepted (not an int)

  return 0;
}

Notice how the boolean condition is defined as follows (omitting std:: to for better readability):

is_same<int,typename decay<T>::type>::value

The decay statement is used to ensure this works regardless of whether T is int or int & (and a few more special cases).


Further remarks: This sort of trick is only really useful if the definition of the function in question is the same for both rvalue and lvalue. In many cases that won't be the case (because the rvalue case will implement a move, the lvalue won't, or something similar).

A typical situation where both definitions are in fact the same is when the function body is very short and does nothing but forwarding the argument(s) to yet another (possibly overloaded) function call:

template <typename T>
void fun(T &&obj)
{ other_fun(std::forward<T>(obj)); }

In that case it is probably ok not to use any enable_if or other trick, because the declaration of other_fun will ensure that ultimately only certain types will be accepted.

like image 96
jogojapan Avatar answered Sep 30 '22 02:09

jogojapan


Use std::enable_if with std::is_same:

template<class T, 
         class = typename std::enable_if<std::is_same<float, T>::value>::type>
void function(T&& t){/*SNIP*/}

And (as first stated in jogojapan's answer, which I forgot to mention) use std::decay on T, as T& is not the same type as a T and const T is not the same as T`:

template<
  class T, 
  class = typename std::enable_if<
                       std::is_same<float,
                                    typename std::decay<T>::type>::value>::type>
void function(T&& t){/*SNIP*/}

See demo at http://ideone.com/ztkHsf .

like image 27
hmjd Avatar answered Sep 30 '22 00:09

hmjd