2014-11-28 11 views
5

Ich habe eine FunktionUnbekannt Rückgabetyp in Vorlage

template <typename T1, typename T2> 
/*return type*/ foo(MyClass<T1>& bar1, MyClass<T2>& bar2) 
{ 
    if (something) 
     return bar1; 
    else 
     return bar2; 
} 

Das Problem ist, dass ich weiß nicht, was wird diese Funktion Rückkehr: es entweder MyClass<T1> oder MyClass<T2> sein kann.

Wie kann ich es zur Arbeit bringen?

T1 und T2 sind Strukturen von 3 Ints, die zur Kompilierungszeit bekannt sind. Der Rückgabetyp hängt davon ab, kleiner dieser 2: zum Beispiel für T1 = <5, 1, 1>T2 = <4, 4, 7> Rückgabetyp sollte MyClass<T2> sein.

Beispiel Nutzung:

MyClass<5, 2, 8> m1; 
MyClass<4, 2, 1> m2; 
cout << foo(m1, m2); //should print m2 (I have a method used for that) 
+4

Ist 'something' eine Kompilierung oder zur Laufzeit Zustand? –

+0

Ja gut zu wissen, ob es Kompilierzeit oder Laufzeit ist ... – AlexTheo

+0

Bearbeitet, um diese Frage zu beantworten. –

Antwort

2

Sie können zwei Funktionen definieren, aus denen nur einer wird für bestimmte Typen instanziiert werden:

template <typename T1, typename T2> 
struct ChooseFirst; 

template <int A1, int B1, int C1, int A2, int B2, int C2> 
struct ChooseFirst<MyClass<A1, B1, C1>, MyClass<A2, B2, C2>> { 
    // this requires constexpr make_tuple (C++14) 
    static const bool value = std::make_tuple(A1, B1, C1) < std::make_tuple(A2, B2, C2); 
    // if in your implementation make_tuple is not constexpr, you can write the comparison manually: 
    // static const bool value = A1 < A2 || (A1 == A2 && (B1 < B2 || (B1 == B2 && C1 < C2))); 
}; 

template <typename T1, typename T2> 
typename std::enable_if<ChooseFirst<T1, T2>::value, T1&>::type foo(T1& bar1, T2&) { 
    return bar1; 
} 

template <typename T1, typename T2> 
typename std::enable_if<!ChooseFirst<T1, T2>::value, T2&>::type foo(T1&, T2& bar2) { 
    return bar2; 
} 

Demo

1

Sie könnten zurückgeben union Art, die entweder T1 oder T2 sein kann.

4

Was Sie dort erreichen wollen, kann nicht so gemacht werden, wie es ist. Die Sprache erlaubt Ihnen nicht, eine Funktion zu haben, die auf der Grundlage einiger Laufzeitdaten ihren Rückgabetyp ändert. Abhängig vom Problem, das Sie zu lösen versuchen, gibt es möglicherweise verschiedene Alternativen, z. B. einen allgemeinen Typ zu bestimmen, der entweder bar1 oder bar2 oder den Code reorganisieren kann, so dass Sie überhaupt nicht zurückkehren müssen.

Das ist, wenn something nicht zur Kompilierzeit bestimmt werden kann ... wenn es bestimmt werden kann, können Sie einige Metaprogrammierung an Ort und Stelle haben, um den Rückgabetyp zu bestimmen, der Sie benötigen.

Sie sollten eine höhere Beschreibung des realen Problems angeben, nach der Sie bessere Ideen bekommen könnten, in welche Richtung Sie gehen sollten.


Sie können etwas in den Zeilen versuchen:

template <bool value> struct Bool {}; 

template <typename T, typename U> 
T f_impl(T t, U u, Bool<true>) { return t; } 
template <typename T, typename U> 
T f_impl(T t, U u, Bool<false>) { return u; } 

template <int A, int B, int C, int D, int E, int F> 
auto f(MyClass<A,B,C> a, MyClass<D,E,F> b) 
    -> f_impl(a, b, Bool<!(A < D 
        || (A == D && B < E) 
        || (A == D && B == E && C < F))>()) 
{ 
    return f_impl(a, b, Bool<!(A < D 
          || (A == D && B < E) 
          || (A == D && B == E && C < F))>()); 
} 
+0

Ich Eddited Frage, um mehr Informationen über T1 und T2 zu bieten. –

+0

@MDo: Interessiert Sie der Wert oder nur der Typ, der zurückgegeben wird? (dh was ist wirklich 'MyClass'? Kann andere' MyClass 'unterschiedliche Werte für das gleiche' X, Y, Z' Tuple halten? Die Beschreibung * Strukturen von 3 Ints bekannt zur Kompilierzeit * ist mir nicht klar. –

+0

Ja Das kann, aber es ist einfach, diese mithilfe eines Konstruktors zu setzen - wenn wir wüssten, welchen Typ wir verwenden möchten, könnten wir einfach ein Objekt erstellen, das zurückgegeben wird. –

1

Da der Rückgabetyp muss als der Kompilierung festgelegt werden, müssen Sie etwas zurückgeben, die entweder MyClass<T1> oder MyClass<T2> sein kann. Dies könnte entweder ein generisches Objekt wie boost::any sein (ein bisschen Overkill für diese Situation) oder eine gemeinsame Basis, eine Referenz (oder ein Zeiger), auf die Sie dann zurückkommen. Dies erfordert Ihre Klassen wie

definiert werden
class MyClassBase { /* ... */ }; 
template<typename T> 
class MyClass : MyClassBase { /* ... */ }; 

template<typename T1, typename T2> 
MyClassBase& foo(MyClass<T1>&bar1,MyClass<T2>&bar2) 
{ 
    if (something) 
     return bar1; 
    else 
     return bar2; 
} 

können Sie tatsächlich RTTI verwenden, so dass die Basis MyClassBase sagen kann, was es tatsächlich ist. In der Tat ist dies ungefähr wie boost::any funktioniert.


Natürlich, wie David sagte, wenn something zum Zeitpunkt der Kompilierung bekannt ist, dann ist der Code nicht wirklich ein gutes Design, und Sie sollten stattdessen ein anderes verwenden, Compiler-Lösungen mit (via Template Meta- Programmiertechniken).

2

würde ich vorschlagen, einen Rückruf statt Rückkehr Wert zu machen, erinnern Sie sich, fragen Sie nicht Prinzip:

template<typename T> 
struct op{ 
    void operator()(T t){} 
}; 

template<> 
struct op<int>{ 
    void operator()(int a){} 
}; 

template<typename T> 
struct Func : public op<T>{ 
    int a; 
}; 

template<typename T, typename T2> 
void foo(Func<T> t, Func<T2> t2){ 
    t.a = 3; 
    t2.a = 4; 
    if(t.a > t2.a){ 
    t(3); 
    }else{ 
    t2(5); 
    } 
} 

Vielleicht eine bessere Lösung gibt. Oder Sie können den Operator() in MyClass verwenden, nur spezialisieren.

1

Sowohl bar1 als auch bar2 sind Referenzen, so dass sie beide zurückgegeben werden, wenn Sie sie in Ihrer Funktion ändern. Sie könnten einen Rückgabewert verwenden, um anzuzeigen, welche:

enum ReturnType 
{ 
    BAR1, 
    BAR2 
} 

ReturnType foo(MyClass<T1>& bar1, MyClass<T2>& bar2) 
{ 
    if (something) 
     return BAR1; 
    else 
     return BAR2; 
} 

int main() 
{ 
    MyClass<T1> t1; 
    MyClass<T2> t2; 
    ReturnType ret = foo(t1, t2); 
    if(ret == BAR1) 
    { 
     // do what you want in case of MyClass<T1> 
    } 
    else if(ret == BAR2) 
    { 
     // do what you want in case of MyClass<T2> 
    } 
} 

Eine andere Art und Weise, dass vielleicht ist näher an, was Sie wollen, ist eine Basisklasse Zeiger zu verwenden:

class Base 
{ 
} 

template<typename T> 
class MyClass : public Base 
{ 
    // ... 
} 

Base* foo(MyClass<T1>& bar1, MyClass<T2>& bar2) 
{ 
    if (something) 
     return &bar1; 
    else 
     return &bar2; 
} 

Genau wie in dem von vsoftco erwähnt Kommentare und in Walters Antwort die Rückgabe einer Referenz funktioniert auch, wenn das Ihre Präferenz ist.

Base& foo(MyClass<T1>& bar1, MyClass<T2>& bar2) 
{ 
    if (something) 
     return bar1; 
    else 
     return bar2; 
} 
+0

Dies tut es nicht, da Sie den Typ 'MyClass ' nicht wirklich zurückgeben, sondern nur eine Enum-Konstante, die angibt, welchen Typ Sie retu haben sollten rned. – vsoftco

+0

Die aktualisierte Bearbeitung sollte funktionieren, entfernt -1. Sie könnten auch einfach eine Referenz 'Base &' – vsoftco

+0

zurückgeben Ich fügte eine andere Idee hinzu, um zu betrachten und auch, wie ich für die ursprüngliche Idee beabsichtigte, verwendet zu werden. Ich weiß, dass es nicht genau das ist, was gefragt wurde, aber es ist eine Arbeit, da das, was gefragt wird, in C++ nicht möglich ist – warsac