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


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
Функція може повертати лише один фіксований тип. Ви не можете вибрати під час виконання, який тип повернути.
Jesper Juhl

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коли ми передаємо 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+ ternary). Справжня сила Росії std::common_typeполягає в тому, що він приймає будь-яку кількість параметрів.


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

Побачити в прямому ефірі


Попередній тип повернення не потрібен, принаймні, як для C ++ 14.
Soundsish

@walnut Хороший момент. Ще один варіант - це посилання на переадресацію?
JeJo

1
@JeJo Так, я вважаю, що це добре, але, ймовірно, безглуздо, тому що ви не змінюєте жодного аргументу, і тип повернення все ще буде посиланням на значення в будь-якому випадку (хоча потенційно не є const).
волоський горіх

Я видалив свої коментарі, оскільки вони більше не застосовуються, але я б запропонував додати попередження у відповідь, що ви не можете приймати параметри за значенням.
волоський горіх

Якщо хтось дивиться на ваш код, здається, що переданий параметр вирішить, який тип повернення хтось отримає, а це не так! Ви завжди отримаєте подвійний, навіть якщо a більший за b.
Клаус

4

Позначаючи тип повернення як Yі передаючи знак intяк другий параметр, ви чітко вказали, що Yце "an" 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)може повертати посилання на типи. Якщо ви повернете щось локальне до функції (кількість аргументів), ви отримаєте невизначене поведінку.


@walnut Випадково повернути посилання набагато важче, ніж це робить цей сайт. Але добре знати про невизначену поведінку. Це не так, як це був би код, який я б все одно написав; це відповідь на запитання.
Soundsish

1
Ага. Я читав ваш попередній коментар як два чіткі моменти, а не ефект і причина. Я можу внести відповідну редакцію.
Soundsish

Я додав додаткову відмову від відповідальності.
сивий

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. Коли ви називали великими 32,82, це поплавок, але 8 - це int, а тип повернення функції - Y, що також є int.

Ви не можете реально виправити це, як вам потрібно знати під час виконання, який тип великої віддачі, тому зробіть а і 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);
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.