3

the problem is described briefly as below:

template <typename T>
void display(T data){
    if(is_int(T)) // how to check if T is int in this function is_int
        printf("<int> %d", data);
    if(is_float(T)) // how to check if T is float in this function is_float
        printf("<int> %f", data);
    if(is_class(T)) // how to check if T is class in this function is_class
        data.display();
}

Here suppose that T can be type of int or float or a class.

If I define some variables and want to display their values using the same function:

int a = 10:
float b = 2.7;
A_Class c;

display(a);
display(b);
display(c);
display(new int(3));
display(new float(1.899));
display(new float(1));

I know that in C++, there is one solution for checking int and float(just for the issue of printing), that is to use std::cout, as explained in this question(C++ templates - How to find whether the template type is a basic type or a class).

And using std::is_integral::value doesn't apply for the case like this:

display(new int(3));
display(new float(1.899));
display(new float(1));

because these variables are classes not the basic types. So for this situation, how we judge the type(int or float) of new int(), new float()?

Community
  • 1
  • 1
qingl97
  • 327
  • 4
  • 14
  • 1
    The usual way to do this is to actually specialize the template on the type. You can even create one to handle classes based on whether the class has a member function, like `display()`. – woolstar Jan 07 '14 at 18:27
  • 2
    in your example you might just want to overload your function. I'm not sure what benefits you get out of templates here. – Red Alert Jan 07 '14 at 18:33

3 Answers3

8

To print the int and float values just provide overloads of display() that take arguments of those types, respectively. For objects that contain a member function named display(), you can use SFINAE to selectively enable the free function form of display()

#include <iostream>
#include <type_traits>

template<typename T>
auto display(T const& t) -> decltype(t.display(), void())
{
    std::cout << "display(T const& t)\n";
}

void display(int)
{
    std::cout << "display(int)\n";
}

void display(double)
{
    std::cout << "display(double)\n";
}

struct foo
{
    void display() const
    {
        std::cout << "foo::display\n";
    }
};

struct bar {};

int main()
{
    display(10);
    display(10.0);
    display(foo());
//    display(bar()); // doesn't compile
}

Live demo of what happens when you call display(bar());

main.cpp:35:18: error: no matching function for call to 'display(bar)'

     display(bar()); // doesn't compile
...
main.cpp:5:49: error: 'const struct bar' has no member named 'display'
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • Could you clarify something in this answer? I use trailing return syntax and decltype quite a bit, but your use of it in the template is unfamiliar to me. Specifically this: "-> decltype(t.display(), void()) " I would expect it to be "decltype(t.display())" What is with the extra command and void ", void()" How is that even syntactically valid and what does it mean, in rough terms? – Joe Jan 06 '18 at 21:35
  • 1
    @Joe `-> decltype(t.display())` alone would mean the return type of `display` is the same as that of `t.display()`. `, void()` is using the comma operator to add a second subexpression `void()`, so that the complete `decltype` expression yields a return type of `void`. See [this answer](https://stackoverflow.com/a/23988669/241631) where I've tried to explain it in detail (although that is a more complicated case than this one). – Praetorian Jan 08 '18 at 17:19
  • Even after 25 years of C++, my brain can still be made to explode. Trailing return syntax as a trigger for SFINAE with the comma operator to achieve multiple objectives at once. Brilliant. – Joe Jan 09 '18 at 16:27
4

You provide the versions directly, check are provided by <type_traits>:

template <typename T>
typename std::enable_if<std::is_same<T, int>::value>::type
display(T data){
    printf("<int> %d", data);
}

template <typename T>
typename std::enable_if<std::is_same<T, float>::value>::type
display(T data){
    printf("<int> %f", data);
}

template <typename T>
typename std::enable_if<std::is_class<T>::value>::type
display(const T& data){ // you probably don't want to copy the argument
    data.display();
}
Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • 3
    A better approach is to use overloads for `int`, `float` and then a template for the rest – David Rodríguez - dribeas Jan 07 '14 at 18:33
  • 1
    @Mgetz: Well, not exactly as it is. If you specialize a template you must provide the same interface, that means settling for const reference in all cases (the penalty of passing `int` or `float` by reference seems to be a better alternative than having to pass random types by value). That being said, it is better to avoid template function specializations. – David Rodríguez - dribeas Jan 07 '14 at 18:48
2

One way to achieving is using numeric limits. This is however, to check if it is an integer or a floating point number.

You could do the following:

#include<limits>
template <typename T>
void display(T data){
  if(std::numeric_limits<T>::is_signed) // how to check if T is int in this function is_int
     printf("<int> %d", data);
  else // how to check if T is float in this function is_float
     printf("<int> %f", data);
}
Lokesh A. R.
  • 2,326
  • 1
  • 24
  • 28