Rakshanda Meshram
Rakshanda Meshram

Reputation: 91

How to return correct type of data in templates?

#include <iostream>
using namespace std;

template <class X, class Y>
Y big(X a, Y b)
{
   if (a > b)
      return (a);
   else return (b);
}

int main()
{
   cout << big(32.8, 9);
}

Here I am using templates in CPP, so when I call the function big bypassing arguments of double and int type, I want the return answer which is double. The type here, it returns 32 instead of 32.8.

How I get my desired output? How to write a proper return type of big function?

Upvotes: 9

Views: 440

Answers (5)

sweenish
sweenish

Reputation: 5202

In marking your return type as Y and passing an int as your second parameter, you've clearly indicated that Y is an int. There's no surprises going on here.

#include <iostream>

template <typename X, typename Y>
decltype(auto) big(const X& a, const Y& b)  // return type can just be auto as well 
{
    return a > b ? a : b;
}

int main()
{
    std::cout << big(32.8, 9) << '\n';
    std::cout << big(9, 32.8) << '\n';
    std::cout << big(32.8, 90) << '\n';
    std::cout << big(90, 32.8) << '\n';
}

This prints all four correct values to the screen.

https://godbolt.org/z/fyGsmo

One thing that's important to note is that this will only work for types that can be compared against each other, i.e., the compiler will implicitly convert one type to the other for the comparison.

IMPORTANT: The parameters need to be taken by reference to avoid undefined behavior. This has to do with the return type I am stubbornly sticking by. decltype(auto) can return references to types. If you return something local to the function (arguments count), you get undefined behavior.

Upvotes: 4

JeJo
JeJo

Reputation: 32797

The return type must be determined at compile-time. You can use trailing return with a conditional operator, if you are limited to .

template <typename X, typename Y>
auto big(X&& a, Y&& b) -> decltype(a > b ? a : b) // ---> like this
{
   return  a > b ? a : b;
}

See live


However, if you have access to or higher auto return is enough, as the compiler will deduce the right type if you use it along with the conditional operator as follows:

template <typename X, typename Y>
auto big(X a, Y b)
{
   return  a > b ? a : b;
}

See live

Upvotes: 10

N. Shead
N. Shead

Reputation: 3938

This is not the correct solution for your precise situation, in all likelihood – the other answers are likely to be much closer to what you want.

However, if you really need to return entirely different types at runtime for some reason, the correct solution (since ) is to use a std::variant, which is a sort of type-safe union.

#include <variant>

template <typename X, typename Y>
std::variant<X, Y> max(X a, Y b) {
  if (a > b)
    return std::variant<X, Y>(std::in_place_index_t<0>, a);
  else
    return std::variant<X, Y>(std::in_place_index_t<1>, b);
}

Note that then the onus is on the caller to deal with the returned value, most likely using std::visit or the like.

Upvotes: 3

Dantsz
Dantsz

Reputation: 1

It returns int because Y is an int and it casts the 32.8 to it. When you called big 32,82 is a float, but 8 is an int and the function return type is Y, which is also int.

You can't really fix this as you need to know at runtime which type big returns, so make a and b the same type like this:

    #include <iostream>
    using namespace std;

    template <typename X>

    X big (X a, X b)
    {
    if (a>b)
    return a;

    else return b;
    }

    int main()
    {
    cout<< big (32.8, 9.0);
    }

Upvotes: -2

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122458

A function can have only one return type that has to be known at compile time. However, you can use std::common_type, to return a type that both parameters can be implicity converted to.

That would be

#include <type_traits>
template <class X, class Y>
typename std::common_type<X,Y>::type big(X a, Y b)
{
   if (a > b)
      return a;
   else return b;
}

And to check that it actually returns a double when passed an int and a double we can do:

int main() {
    auto x = big(4.2,42);
    std::cout << std::is_same<decltype(x),double>::value;
}

Which prints

1

PS: std::common_type can use the ternary operator behind the scences and as such this solution isnt much different from the other answers (auto + ternary). The real power of std::common_type is that it accepts any number of parameters.

Upvotes: 12

Related Questions