Wie kann ich den richtigen Datentyp in Vorlagen zurückgeben?


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

Hier verwende ich Vorlagen in CPP. Wenn ich also die Funktion bigaufrufe, die Argumente doubleund intTyp umgeht , möchte ich die Rückantwort, die lautet double. Den Typ hier gibt es 32statt 32.8.

Wie erhalte ich meine gewünschte Ausgabe? Wie schreibe ich einen richtigen Rückgabetyp big?


1
Eine Funktion kann nur einen festen Typ zurückgeben. Sie können zur Laufzeit nicht auswählen, welcher Typ zurückgegeben werden soll.
Jesper Juhl

1
Vielleicht möchten Sie sich ansehen, wie std::maxes implementiert wird. Der Rückgabetyp einer Funktion muss zur Kompilierungszeit in C ++ bekannt sein. Daher kann dieser Rückgabetyp nicht vom Laufzeitwert Ihrer Parameter abhängen. Aus diesem Grund müssen für eine solche Funktion beide Parameter denselben Typ haben (dh Typ X, aber nicht Y).
Boris Dalstein

Antworten:


12

Eine Funktion kann nur einen Rückgabetyp haben, der zur Kompilierungszeit bekannt sein muss. Sie können jedoch std::common_typeeinen Typ zurückgeben, in den beide Parameter implizit konvertiert werden können.

Das wäre

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

Und um sicherzustellen , dass es sich um eine tatsächlich zurückgibt , doublewenn übergeben ein intund doublewir tun können:

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

Welche druckt

1

PS: std::common_typeKann den ternären Operator hinter den Szenen verwenden und als solche unterscheidet sich diese Lösung nicht wesentlich von den anderen Antworten ( auto+ ternär). Die wahre Stärke von std::common_typeist, dass es eine beliebige Anzahl von Parametern akzeptiert.


10

Der Rückgabetyp muss zur Kompilierungszeit festgelegt werden. Sie können die nachfolgende Rückgabe mit einem bedingten Operator verwenden , wenn Sie auf .

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

Live sehen


Wenn Sie jedoch Zugriff auf oder höher haben, reicht die autoRückgabe aus, da der Compiler den richtigen Typ ableitet, wenn Sie ihn zusammen mit dem bedingten Operator wie folgt verwenden:

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

Live sehen


Ein ablaufender Rückgabetyp wird zumindest ab C ++ 14 nicht benötigt.
Zwischen

@walnut Guter Punkt. Eine weitere Option ist die Weiterleitungsreferenz?
JeJo

1
@JeJo Ja, ich nehme an, das ist auch in Ordnung, aber wahrscheinlich sinnlos, da Sie keines der beiden Argumente ändern und der Rückgabetyp in beiden Fällen immer noch eine Wertreferenz ist (obwohl möglicherweise nicht const).
Walnuss

Ich habe meine Kommentare entfernt, da sie nicht mehr zutreffen. Ich würde jedoch vorschlagen, der Antwort eine Warnung hinzuzufügen, dass Sie die Parameter nicht als Wert verwenden können.
Walnuss

Wenn jemand auf Ihren Code schaut, scheint der übergebene Parameter zu entscheiden, welchen Rückgabetyp jemand erhält, was nicht der Fall ist! Sie erhalten immer ein Doppel zurück, auch wenn a größer als b ist.
Klaus

4

Wenn Sie Ihren Rückgabetyp als Yund intals zweiten Parameter markieren , haben Sie klar angegeben, dass dies ein Yist int. Hier gibt es keine Überraschungen.

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

Dadurch werden alle vier korrekten Werte auf dem Bildschirm gedruckt.

https://godbolt.org/z/fyGsmo

Eine wichtige Sache ist, dass dies nur für Typen funktioniert, die miteinander verglichen werden können, dh der Compiler konvertiert implizit einen Typ für den Vergleich in den anderen.

WICHTIG : Die Parameter müssen als Referenz verwendet werden, um undefiniertes Verhalten zu vermeiden. Dies hat mit dem Rückgabetyp zu tun, an dem ich hartnäckig festhalte. decltype(auto)kann Verweise auf Typen zurückgeben. Wenn Sie der Funktion etwas Lokales zurückgeben (Anzahl der Argumente), erhalten Sie ein undefiniertes Verhalten.


@walnut Das versehentliche Zurücksenden einer Referenz ist viel schwieriger, als es diese Site vermuten lässt. Aber gut zu wissen über das undefinierte Verhalten. Es ist nicht so, dass dies Code wäre, den ich sowieso schreiben würde. Es ist eine Antwort auf eine Frage.
Zwischen

1
Ah. Ich habe Ihren früheren Kommentar als zwei unterschiedliche Punkte gelesen und nicht als Wirkung und Ursache. Ich kann die entsprechende Bearbeitung vornehmen.
Zwischen

Ich habe einen zusätzlichen Haftungsausschluss hinzugefügt.
Zwischen

2

Dies ist aller Wahrscheinlichkeit nach nicht die richtige Lösung für Ihre genaue Situation - die anderen Antworten sind wahrscheinlich viel näher an Ihren Wünschen.

Wenn Sie jedoch aus irgendeinem Grund zur Laufzeit wirklich ganz andere Typen zurückgeben müssen, besteht die richtige Lösung (seit ) darin, a zu verwenden std::variant, was eine Art typsichere Vereinigung ist.

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

Beachten Sie, dass es dann Sache des Anrufers ist, mit dem zurückgegebenen Wert umzugehen, höchstwahrscheinlich mit std::visitoder ähnlichem.


-2

Es gibt int zurück, weil Y ein int ist und es die 32.8 in es umwandelt. Wenn Sie big 32,82 genannt haben, ist dies ein float, aber 8 ist ein int und der Funktionsrückgabetyp ist Y, was auch int ist.

Sie können dies nicht wirklich beheben, da Sie zur Laufzeit wissen müssen, welcher Typ große Renditen liefert. Machen Sie also a und b zum selben Typ:

    #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);
    }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.