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&&){}
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.
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 .
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With