Come restituire il tipo corretto di dati nei modelli?


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

Qui sto usando i modelli in CPP, quindi quando chiamo la funzione bigbypassando argomenti doublee inttipo, voglio la risposta di ritorno che è double. Il tipo qui, restituisce 32invece di 32.8.

Come ottengo l'output desiderato? Come scrivere un corretto tipo di bigfunzione di ritorno ?


1
Una funzione può restituire solo un tipo fisso. Non è possibile scegliere in fase di esecuzione quale tipo restituire.
Jesper Juhl,

1
Potresti voler vedere come std::maxviene implementato. Il tipo restituito di una funzione deve essere noto al momento della compilazione in C ++. Pertanto, questo tipo di ritorno non può dipendere dal valore di runtime dei parametri. Questo è il motivo per cui per tale funzione, è necessario che entrambi i parametri abbiano lo stesso tipo (ovvero, abbiano tipo X, ma non Y).
Boris Dalstein,

Risposte:


12

Una funzione può avere un solo tipo restituito che deve essere noto al momento della compilazione. Tuttavia, è possibile utilizzare std::common_typeper restituire un tipo in cui entrambi i parametri possono essere convertiti in modo implicito.

Sarebbe

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

E per verificare che ritorni effettivamente a doublequando passato an inte a doublepossiamo fare:

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

Quale stampa

1

PS: std::common_typepuò usare l'operatore ternario dietro i profumi e come tale questa soluzione non è molto diversa dalle altre risposte ( auto+ ternario). Il vero potere di std::common_typeè che accetta un numero qualsiasi di parametri.


10

Il tipo restituito deve essere determinato in fase di compilazione. È possibile utilizzare il ritorno finale con un operatore condizionale , se si è limitati a .

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

Vedi dal vivo


Tuttavia, se hai accesso a o versioni successive autoè sufficiente, poiché il compilatore dedurrà il tipo giusto se lo usi insieme all'operatore condizionale come segue:

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

Vedi dal vivo


Il tipo di ritorno finale non è necessario, almeno a partire da C ++ 14.
sweenish

@walnut Un buon punto. un'altra opzione è il riferimento di inoltro?
JeJo

1
@JeJo Sì, suppongo che vada bene, ma probabilmente è inutile, perché non stai modificando nessuno degli argomenti e il tipo restituito sarà comunque un riferimento lvalue in entrambi i casi (anche se potenzialmente non const).
noce

Ho rimosso i miei commenti poiché non si applicano più, ma suggerirei di aggiungere un avviso alla risposta che non è possibile prendere i parametri per valore.
noce

Se qualcuno cerca il tuo codice, sembra che il parametro passato deciderà quale tipo di ritorno otterrà qualcuno che non è il caso! Riceverai sempre un doppio, anche se a è maggiore di b.
Klaus

4

Nel contrassegnare il tipo restituito come Ye passare un intcome secondo parametro, hai chiaramente indicato che Yè un int. Non ci sono sorprese in corso qui.

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

Ciò stampa tutti e quattro i valori corretti sullo schermo.

https://godbolt.org/z/fyGsmo

Una cosa importante da notare è che funzionerà solo per tipi che possono essere confrontati tra loro, cioè il compilatore convertirà implicitamente un tipo nell'altro per il confronto.

IMPORTANTE : i parametri devono essere presi come riferimento per evitare comportamenti indefiniti. Questo ha a che fare con il tipo di ritorno a cui sto ostinatamente attaccando. decltype(auto)può restituire riferimenti a tipi. Se si restituisce qualcosa di locale alla funzione (gli argomenti contano), si ottiene un comportamento indefinito.


@walnut La restituzione accidentale di un riferimento è molto più difficile di quanto questo sito sembri. Ma buono a sapersi del comportamento indefinito. Non è che questo sarebbe il codice che scrivo comunque; è una risposta a una domanda.
svedese il

1
Ah. Ho letto il tuo commento precedente come due punti distinti e non effetto e causa. Posso apportare la modifica appropriata.
svedese il

Ho aggiunto una dichiarazione di non responsabilità aggiuntiva.
svedese il

2

Questa non è la soluzione corretta per la tua situazione precisa, con ogni probabilità - le altre risposte sono probabilmente molto più vicine a ciò che desideri.

Tuttavia, se hai davvero bisogno di restituire tipi completamente diversi in fase di runtime per qualche motivo, la soluzione corretta (dal ) è quella di usare a std::variant, che è una sorta di unione di tipo sicuro.

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

Si noti che quindi l'onere è sul chiamante per gestire il valore restituito, molto probabilmente usando std::visito simili.


-2

Restituisce int perché Y è un int e vi lancia il 32.8. Quando hai chiamato big 32,82 è un float, ma 8 è un int e il tipo restituito dalla funzione è Y, che è anche int.

Non puoi davvero risolvere questo problema in quanto devi sapere in fase di esecuzione quale tipo restituisce grandi, quindi fai aeb lo stesso tipo in questo modo:

    #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);
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.