Как вернуть правильный тип данных в шаблоны?

9
#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);
}

Здесь я использую шаблоны в CPP, поэтому, когда я вызываю функцию, bigобходя аргументы типа doubleи int, я хочу получить ответ, который есть double. Тип здесь, он возвращает 32вместо 32.8.

Как получить желаемый результат? Как написать правильный тип возвращаемого значения bigфункции?

Ракшанда Мешрам
источник
1
Функция может возвращать только один фиксированный тип. Вы не можете выбрать во время выполнения, какой тип возвращать.
Джеспер Юл
1
Вы можете посмотреть, как std::maxэто реализовано. Тип возврата функции должен быть известен во время компиляции в C ++. Таким образом, этот тип возврата не может зависеть от значения времени выполнения ваших параметров. Вот почему для такой функции вам нужно, чтобы оба параметра имели одинаковый тип (т. Е. Имели тип X, но не Y).
Борис Дальштейн

Ответы:

12

Функция может иметь только один тип возвращаемого значения, который должен быть известен во время компиляции. Тем не менее, вы можете использовать std::common_type, чтобы вернуть тип, в который оба параметра могут быть безразлично преобразованы.

Это было бы

#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;
}

И чтобы проверить, что он на самом деле возвращает a doubleкогда передано a intи a, doubleмы можем сделать:

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

Какие отпечатки

1

PS: std::common_typeможет использовать троичного оператора за кулисами, и, таким образом, это решение не сильно отличается от других ответов ( auto+ троичный). Реальная сила в std::common_typeтом, что он принимает любое количество параметров.

idclev 463035818
источник
10

Тип возврата должен быть определен во время компиляции. Вы можете использовать конечный возврат с условным оператором , если вы ограничены в .

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

Смотреть в прямом эфире


Однако, если у вас есть доступ к или выше, autoдостаточно возврата, поскольку компилятор выведет правильный тип, если вы используете его вместе с условным оператором, следующим образом:

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

Смотреть в прямом эфире

JeJo
источник
Конечный тип возврата не требуется, по крайней мере, в C ++ 14.
между
@walnut Хороший вопрос. Еще один вариант - это ссылка для пересылки?
JeJo
1
@JeJo Да, я полагаю, что это тоже хорошо, но, вероятно, бессмысленно, потому что вы не изменяете ни один из аргументов, и возвращаемый тип все равно будет ссылкой на lvalue в любом случае (хотя потенциально не const).
грецкий орех
Я удалил свои комментарии, так как они больше не применяются, но я бы предложил добавить к ответу предупреждение о том, что вы не можете принимать параметры по значению.
грецкий орех
Если кто-то просматривает ваш код, кажется, что переданный параметр решит, какой тип возвращаемого значения кто-то получит, а это не так! Вы всегда получите двойной, даже если a больше, чем b.
Клаус
4

Помечая тип возвращаемого значения как Yи передавая intвторой параметр, вы четко указали, что Yэто int. Здесь нет никаких сюрпризов.

#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';
}

Это выведет все четыре правильных значения на экран.

https://godbolt.org/z/fyGsmo

Важно отметить, что это будет работать только для типов, которые можно сравнивать друг с другом, т. Е. Компилятор неявно преобразует один тип в другой для сравнения.

ВАЖНО : параметры должны быть взяты по ссылке, чтобы избежать неопределенного поведения. Это связано с типом возврата, который я упрямо придерживался. decltype(auto)может возвращать ссылки на типы. Если вы возвращаете что-то локальное для функции (количество аргументов), вы получаете неопределенное поведение.

sweenish
источник
@walnut Случайно вернуть ссылку намного сложнее, чем на этом сайте. Но полезно знать о неопределенном поведении. В любом случае, это не тот код, который я бы написал; это ответ на вопрос.
между
1
Ах. Я читаю ваш предыдущий комментарий как два разных момента, а не следствие и причину. Я могу сделать соответствующее редактирование.
между
Я добавил дополнительный отказ от ответственности.
между
2

Это не правильное решение для вашей конкретной ситуации, по всей вероятности - другие ответы, вероятно, будут гораздо ближе к тому, что вы хотите.

Однако, если вам действительно нужно по какой-то причине возвращать совершенно разные типы во время выполнения, правильным решением (начиная с ) является использование a std::variant, что является своего рода безопасным для типов объединением.

#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);
}

Обратите внимание, что тогда на вызывающей стороне лежит обязанность иметь дело с возвращаемым значением, скорее всего, с использованием std::visitили тому подобное.

Н. Шеад
источник
-2

Он возвращает int, потому что Y является int, и он возвращает ему 32.8. Когда вы вызываете big 32,82 - это число с плавающей точкой, но 8 - это int, а тип возвращаемого значения - Y, который также является int.

Вы не можете исправить это, так как вам нужно знать во время выполнения, какой тип возвращает большие значения, поэтому создайте a и b одинакового типа:

    #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);
    }
Danuz991
источник