Comment renvoyer le type de données correct dans les modèles?

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

Ici, j'utilise des modèles dans CPP, donc quand j'appelle la fonction en bigcontournant les arguments de doubleet inttype, je veux la réponse de retour qui estdouble . Le type ici, il renvoie 32au lieu de 32.8.

Comment obtenir la sortie souhaitée? Comment écrire un type de bigfonction de retour approprié ?

Rakshanda Meshram
la source
1
Une fonction ne peut renvoyer qu'un seul type fixe. Vous ne pouvez pas choisir au moment de l'exécution quel type renvoyer.
Jesper Juhl
1
Vous voudrez peut-être voir comment std::maxest mis en œuvre. Le type de retour d'une fonction doit être connu au moment de la compilation en C ++. Vous ne pouvez donc pas faire dépendre ce type de retour de la valeur d'exécution de vos paramètres. C'est pourquoi pour une telle fonction, vous avez besoin des deux paramètres pour avoir le même type (c'est-à-dire, avoir le type X, mais pas Y).
Boris Dalstein le

Réponses:

12

Une fonction ne peut avoir qu'un seul type de retour qui doit être connu au moment de la compilation. Cependant, vous pouvez utiliserstd::common_type pour renvoyer un type dans lequel les deux paramètres peuvent être convertis implicitement.

Ce serait

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

Et pour vérifier qu'il retourne réellement un doublelorsqu'il est passé un intet un, doublenous pouvons le faire:

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

Qui imprime

1

PS: std::common_typepeut utiliser l'opérateur ternaire derrière les scences et en tant que telle, cette solution n'est pas très différente des autres réponses ( auto+ ternaire). Le vrai pouvoir de std::common_typec'est qu'il accepte n'importe quel nombre de paramètres.

idclev 463035818
la source
10

Le type de retour doit être déterminé au moment de la compilation. Vous pouvez utiliser le retour de fin avec un opérateur conditionnel , si vous êtes limité à .

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

Voir en direct


Cependant, si vous avez accès à ou un autoretour supérieur est suffisant, car le compilateur déduira le bon type si vous l'utilisez avec l'opérateur conditionnel comme suit:

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

Voir en direct

JeJo
la source
Le type de retour de fin n'est pas nécessaire, au moins à partir de C ++ 14.
sweenish
@walnut Bon point. une autre option est la référence de transfert?
JeJo
1
@JeJo Oui, je suppose que c'est bien aussi, mais probablement inutile, car vous ne modifiez aucun des deux arguments et le type de retour sera toujours une référence lvalue dans les deux cas (bien que potentiellement non const).
noyer
J'ai supprimé mes commentaires car ils ne s'appliquent plus, mais je suggère d'ajouter un avertissement à la réponse que vous ne pouvez pas prendre les paramètres par valeur.
noyer
Si quelqu'un regarde votre code, il semble que le paramètre passé décidera du type de retour que quelqu'un obtiendra, ce qui n'est pas le cas! Vous récupérerez toujours un double, même si a est plus grand que b.
Klaus
4

En marquant votre type de retour comme Yet en passant un intcomme second paramètre, vous avez clairement indiqué qu'il Ys'agit d'un int. Il n'y a pas de surprise ici.

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

Cela imprime les quatre valeurs correctes à l'écran.

https://godbolt.org/z/fyGsmo

Une chose importante à noter est que cela ne fonctionnera que pour les types qui peuvent être comparés les uns aux autres, c'est-à-dire que le compilateur convertira implicitement un type en un autre pour la comparaison.

IMPORTANT : Les paramètres doivent être pris par référence pour éviter un comportement indéfini. Cela a à voir avec le type de retour que je tiens obstinément. decltype(auto)peut renvoyer des références aux types. Si vous renvoyez quelque chose de local à la fonction (nombre d'arguments), vous obtenez un comportement indéfini.

sweenish
la source
@walnut Renvoyer accidentellement une référence est beaucoup plus difficile que ce site ne le semble. Mais bon à savoir sur le comportement indéfini. Ce n'est pas comme si ce serait du code que j'écrirais de toute façon; c'est une réponse à une question.
sweenish
1
Ah. J'ai lu votre commentaire précédent comme deux points distincts et non comme effet et cause. Je peux faire le montage approprié.
sweenish
J'ai ajouté un avertissement supplémentaire.
sweenish
2

Ce n'est pas la bonne solution pour votre situation précise, selon toute vraisemblance - les autres réponses seront probablement beaucoup plus proches de ce que vous voulez.

Cependant, si vous avez vraiment besoin de retourner des types entièrement différents au moment de l'exécution pour une raison quelconque, la bonne solution (depuis ) consiste à utiliser a std::variant, qui est une sorte d'union de type sécurisé.

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

Notez qu'alors il incombe à l'appelant de traiter la valeur retournée, très probablement en utilisant std::visitou similaire.

N. Shead
la source
-2

Il retourne int car Y est un int et il lui envoie le 32.8. Lorsque vous appelez big 32,82 est un flottant, mais 8 est un int et le type de retour de la fonction est Y, qui est également int.

Vous ne pouvez pas vraiment résoudre ce problème car vous devez savoir à l'exécution quel type de gros retours, alors faites a et b le même type comme ceci:

    #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
la source