2

Is there a way that I can use std::numeric_limits<T>::is_integer and std::numeric_limits<T>::is_specialized to change template behavior?

For example can I do this:

template < typename T >
void foo( const T& bar )
{
    if( std::numeric_limits< T >::is_integer )
    {
        isInt( bar );
    }
    else if( std::numeric_limits< T >::is_specialized )
    {
        isFloat( bar );
    }
    else
    {
        isString( bar );
    }
}
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288

2 Answers2

8

What you have is currently valid. However, you should prefer to use SFINAE and <type_traits> instead since it would dispatch to a different function based on the type rather than rely on a branch condition (which may or may not be optimized away).

You can use std::enable_if to do the following:

template<typename T, 
         typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void foo(const T& t) {
    isInt(t);
}

template<typename T, 
         typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
void foo(const T& t) {
    isFloat(t);
}

template<typename T, 
         typename std::enable_if<!std::is_integral<T>::value && 
                                 !std::is_floating_point<T>::value, int>::type = 0>
void foo(const T& t) {
    isString(t);
}

Live Demo

The reason that the second parameter for enable_if is set to int is to save us some typing. If the int is left out then we'd have to do typename = typename std::enable_if<std::is_integral<T>::value>::type instead of just setting it to 0 which would save us a couple of characters to type. They're equivalent for all intents and purposes.

Milan
  • 1,743
  • 2
  • 13
  • 36
Rapptz
  • 20,807
  • 5
  • 72
  • 86
  • That second parameter to `enable_if` you always have as an `int`. I don't understand why. – Jonathan Mee Aug 11 '14 at 20:10
  • @JonathanMee The `int` for the second parameter is there so you can set it equal to something (in this case `0`) that way you don't have to type `typename` redundantly, i.e. `typename = typename std::enable_if::type` – Rapptz Aug 11 '14 at 20:12
  • So if I'm understanding you correctly you'd probably want each of these `= 0` to be different (like `= 1`, `= 2`, and `= 3`) so you could reuse them in another method template? – Jonathan Mee Aug 11 '14 at 20:18
  • @JonathanMee No, the condition just has to be different and unambiguous. You can set the number to whatever you want. – Rapptz Aug 11 '14 at 20:19
  • I'm on VS2012 when trying this: `template < typename T, typename std::enable_if< std::is_integral< T >::value, int >::type = 0 > void foo( const T& bar ){ isInt( bar ); }` but I'm getting the error: "error C4519: default template arguments are only allowed on a class template" – Jonathan Mee Aug 11 '14 at 20:46
  • @JonathanMee The linked example compiles on VS2013. You should be able to get it to work on VS2012 if you move the `enable_if` expression to the return type of `foo`. And another alternative to using `int` within `enable_if` is `typename std::enable_if::value>::type* = nullptr` – Praetorian Aug 11 '14 at 21:11
  • 2
    Why don't you just use `enable_if` to define the return type? It is much less typing that way (`template typename std::enable_if::value>::type foo`…). – Jan Hudec Aug 13 '14 at 12:05
4

The "obvious" answer is that you could use something like std::enable_if.

For example:

template<typename T>
typename std::enable_if<std::numeric_limits<T>::is_integer, void>::type
    foo(const T &bar) { isInt(bar); }
template<typename T>
typename std::enable_if<std::numeric_limits<T>::is_specialized, void>::type
    foo(const T &bar) { isFloat(bar); }

The problem with this approach is that this is ambiguous for (as an example) an int parameter, since numeric_limits<int>::is_specialized == true.

To resolve this, I would simply use a better trait than numeric_limits, personally. You can also use boolean conditions to test for the exact condition you want:

template<typename T>
typename std::enable_if<std::numeric_limits<T>::is_specialized && !std::numeric_limits<T>::is_integer, void>::type
    foo(const T &bar) { isFloat(bar); }
kestrel
  • 1,314
  • 10
  • 31
  • It looks like this would help if my functions varied by return type. I want them to vary by parameter type. – Jonathan Mee Aug 11 '14 at 20:29
  • @Praetorian After understanding that the `std::enable_if` here was defining the return type as `void`, I tried this code out. I am able to get this to compile on VS2012. Perhaps I'm misusing @Rapptz's solution since I can't understand what the equality in the `template` parameters is doing. – Jonathan Mee Aug 12 '14 at 11:35
  • @JonathanMee It's not equality, the `= 0` is the default template argument for the non-type template parameter of type `int`. BTW, trying to ping me in comments to other answers doesn't work. I only saw this because you linked to this question in your new question :) – Praetorian Aug 13 '14 at 13:47