Как вернуть правильный тип данных в шаблоны? - PullRequest
9 голосов
/ 15 января 2020
#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?

Ответы [ 5 ]

12 голосов
/ 15 января 2020

Функция может иметь только один тип возвращаемого значения, который должен быть известен во время компиляции. Однако вы можете использовать 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;
}

И чтобы проверить, что он действительно возвращает double при пропуске int и 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 в том, что он принимает любое количество параметров.

10 голосов
/ 15 января 2020

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

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

См. в прямом эфире

4 голосов
/ 15 января 2020

Отмечая тип возвращаемого значения как 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) может возвращать ссылки на типы. Если вы возвращаете что-то локальное для функции (количество аргументов), вы получаете неопределенное поведение.

2 голосов
/ 16 января 2020

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

Однако, если вам действительно нужно чтобы по какой-то причине возвращать совершенно разные типы во время выполнения, правильное решение (поскольку ) заключается в использовании 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 или подобное.

0 голосов
/ 15 января 2020

Возвращает 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);
    }
...