2014-11-28 17 views
5

Ho una funzionetipo restituito sconosciuta nel modello

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

Il problema è che io non so che cosa sarà questa funzione di ritorno: può essere sia MyClass<T1> o MyClass<T2>.

Come posso farlo funzionare?

T1 e T2 sono strutture di 3 inte, note al momento della compilazione. Il tipo di reso dipende dallo più piccolo di quelli 2: ad esempio per T1 = <5, 1, 1>T2 = <4, 4, 7> il tipo di reso deve essere MyClass<T2>.

Esempio utilizzo:

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

è 'something' un tempo di compilazione o condizione di esecuzione? –

+0

Sì, buono per sapere se è tempo di compilazione o runtime ... – AlexTheo

+0

Modificato per rispondere a questa domanda. –

risposta

2

È possibile definire due funzioni da cui vengono istanziati uno solo per determinati tipi:

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

Si potrebbe restituire un tipo union, che può essere sia T1 o T2.

4

Quello che stai cercando di ottenere non può essere fatto così com'è. La lingua non consente di avere una funzione che, sulla base di alcuni dati di runtime, modifica il suo tipo di ritorno. A seconda del problema che si sta tentando di risolvere, potrebbero esserci alternative diverse, come la determinazione di un tipo comune che può essere utilizzato per rappresentare bar1 o bar2 o riorganizzare il codice in modo che non sia necessario restituirlo.

Cioè se something non può essere determinato al momento della compilazione ... se può essere determinato è possibile avere qualche metaprogramming in atto per determinare il tipo di ritorno di essere quello che vi serve.

È necessario fornire una descrizione di livello superiore del problema reale, dopo il quale è possibile ottenere idee migliori su quale direzione prendere.


Si può provare qualcosa nelle linee di:

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

La domanda modificata per fornire ulteriori informazioni su T1 e T2. –

+0

@ MDo: ti interessa il valore o solo il tipo che viene restituito? (es. cosa è veramente 'MyClass'? Può' 'MyClass ' mantenere valori diversi per la stessa tupla 'X, Y, Z? 'La struttura * structs di 3 ints nota al momento della compilazione * non mi è chiara –

+0

Sì può, ma è facile impostare quelli che usano un costruttore - se sapessimo quale tipo vogliamo usare, potremmo facilmente creare un oggetto da restituire. –

1

Dal momento che il tipo di ritorno deve essere fissata come momento della compilazione, è necessario restituire qualcosa che può essere sia MyClass<T1> o MyClass<T2>. Questo potrebbe essere un oggetto generico come boost::any (un po 'eccessivo per questa situazione) o una base comune, un riferimento (o un puntatore) a cui si ritorna. Ciò richiede le vostre classi da definire come

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

Si può effettivamente utilizzare RTTI in modo che la base MyClassBase può dire ciò che effettivamente è. In effetti, questo è grosso modo come funziona boost::any.


Naturalmente, come ha detto David, se something è noto al momento della compilazione, quindi il codice non è davvero un buon design e si dovrebbe invece usare uno diverso, utilizzando soluzioni di compilazione (tramite template meta- tecniche di programmazione).

2

Vorrei suggerire di fare un richiamo al posto del valore di ritorno, ricordo di dire non chiedere principio:

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

Forse c'è una soluzione migliore. Oppure puoi usare l'operatore() in MyClass, solo specializzarlo.

1

Sia bar1 che bar2 sono riferimenti, quindi vengono restituiti entrambi se li si modifica in qualche modo nella propria funzione. Si potrebbe utilizzare un valore di ritorno per indicare quale:

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

Un altro modo che forse è più vicino a ciò che si desidera è di usare un puntatore alla classe base:

class Base 
{ 
} 

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

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

Proprio come menzionato da vsoftco nel commenti e in Walters rispondono restituendo un riferimento funziona anche se è la vostra preferenza.

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

Questo non lo fa, in quanto non si restituisce il tipo 'MyClass ', ma solo una costante enum che indica quale tipo si dovrebbe avere retu rned. – vsoftco

+0

La modifica aggiornata dovrebbe funzionare, rimosso il -1. si potrebbe anche solo restituire un riferimento 'Base &' – vsoftco

+0

Ho aggiunto un'altra idea da considerare e anche come intendevo che l'idea originale fosse usata. So che non è esattamente quello che è stato chiesto, ma è un problema, poiché ciò che viene richiesto non è possibile in C++ – warsac

Problemi correlati