2

I want to write a function

template <class Arg>
tuple<int, double> calc(Arg arg);

It returns:

[arg,0] if arg is int,
[0,arg] if arg is double 
and [0,0] if arg is nor int or double. 

I implement this function comparing the type of arg (Arg), type of i (int) and type of d (double) and then equating i=arg or d=arg respectively. But if I want to use my function with a string or another type, which can not be converted to int/double, I have a conversion error (its clear, cause I can't equate char* to int, for example). How can I bypass this conversion const char* to int (or another inconvertible type to int)? Or maybe there is another implementation of this function?

#define GETTYPE(x) typeid(x).name()
template <class Arg>
    tuple<int,double> calc(Arg arg)
    {
        int i = 0;
        double d = 0;
        if (GETTYPE(arg) == GETTYPE(i))
        {
            i = arg;
        }
        else if (GETTYPE(arg) == GETTYPE(d))
        {
            d = arg;
        }
        return make_tuple(i, d);
    }
Yauhen Mardan
  • 327
  • 3
  • 10
  • 2
    Don't use `typeid::name`. It's much better to use `std::is_same` in this situation – Fureeish Sep 16 '18 at 16:00
  • Please post the exact error message and show how you invoke the template. At a quick glance, `template ` is usually specified as `template `, as `double` and `float` aren't classes. – Ken Y-N Sep 16 '18 at 16:04
  • Thank you, I've fixed it – Yauhen Mardan Sep 16 '18 at 16:05
  • 1
    @KenY-N in this context, `class` and `typename` are treated the same in current standard, so your advice does not apply. `class` will happily be deduced to primitive types. – Fureeish Sep 16 '18 at 16:09

2 Answers2

5

If you do not need a C++17 code, you can use overloading

tuple<int, double> foo(int a) {
    return {a, 0.0};
}

tuple<int, double> foo(double a) {
    return {0, a};
}

template<typename T>
tuple<int, double> foo(T) {
    return {0, 0.0};
}

If you need a C++17 code :

template<typename T>
tuple<int, double> foo([[maybe_unused]] T a) {
    if constexpr (std::is_same_v<int, T>)
        return {a, 0.0};

    else if constexpr (std::is_same_v<double, T>)
        return {0, a};

    else
        return {0, 0.0};
}
Antoine Morrier
  • 3,930
  • 16
  • 37
  • 1
    You need a `constexpr` after your `else if` in your `C++17` version. – Fureeish Sep 16 '18 at 17:00
  • Are you sure? This code compile with clang and gcc and msvc – Antoine Morrier Sep 16 '18 at 18:30
  • have you tried calling `foo("const char* arg")` in main? – Fureeish Sep 16 '18 at 18:30
  • 1
    It really shouldn't. See [my question and the accepted answer](https://stackoverflow.com/questions/52356341/do-i-need-to-put-constexpr-after-else-if) to see why. With `const char*` as an argument, the `return {0, a}` will be compiled (since it's **not** `constexpr`) and even though `std::is_same_v` will yield `false`, the `return` statement has to be compiled. How are you compiling the code and what is **the exact code** you are compiling? – Fureeish Sep 16 '18 at 18:44
  • Okay I used constexpr function. Thanks, I fixed it ;). – Antoine Morrier Sep 16 '18 at 18:55
1

The simplest solution is to just have 3 separate overloads:

tuple<int, double> calc(int arg)
{
  return make_tuple(arg, 0.);
}

tuple<int, double> calc(double arg)
{
  return make_tuple(0, arg);
}

template < typename T >
tuple<int, double> calc(T arg)
{
  return make_tuple(0, 0.);
}

Alternatively you can use template specialisations:

template < typename T >
tuple<int, double> calc(T arg)
{
  return make_tuple(0, 0.);
}

template <>
tuple<int, double> calc< int >(int arg)
{
  return make_tuple(arg, 0.);
}

template <>
tuple<int, double> calc< double >(double arg)
{
  return make_tuple(0, arg);
}
Alan Birtles
  • 32,622
  • 4
  • 31
  • 60