Sovraccaricare una funzione C ++ in base al valore di ritorno

Sappiamo tutti che è ansible sovraccaricare una funzione in base ai parametri:

int mul(int i, int j) { return i*j; } std::string mul(char c, int n) { return std::string(n, c); } 

È ansible sovraccaricare una funzione in base al valore di ritorno? Definire una funzione che restituisce cose diverse in base a come viene utilizzato il valore di ritorno:

 int n = mul(6, 3); // n = 18 std::string s = mul(6, 3); // s = "666" // Note that both invocations take the exact same parameters (same types) 

È ansible assumere che il primo parametro sia compreso tra 0 e 9, non è necessario verificare l’input o avere una gestione degli errori.

 class mul { public: mul(int p1, int p2) { param1 = p1; param2 = p2; } operator int () { return param1 * param2; } operator std::string () { return std::string(param2, param1 + '0'); } private: int param1; int param2; }; 

Non che lo userei.

Devi dire al compilatore quale versione usare. In C ++, puoi farlo in tre modi.

Differenzia in modo esplicito le chiamate digitando

Hai in qualche modo imbrogliato perché hai inviato un intero a una funzione in attesa di un carattere e inviando erroneamente il numero sei quando il valore char di “6” non è 6 ma 54 (in ASCII):

 std::string mul(char c, int n) { return std::string(n, c); } std::string s = mul(6, 3); // s = "666" 

La soluzione giusta sarebbe, ovviamente,

 std::string s = mul(static_cast(54), 3); // s = "666" 

Vale la pena menzionarlo, immagino, anche se non volevi la soluzione.

Differenzia in modo esplicito le chiamate con un puntatore fittizio

È ansible aggiungere un parametro fittizio a ciascuna funzione, forzando così il compilatore a scegliere le giuste funzioni. Il modo più semplice è inviare un puntatore fittizio NULL del tipo desiderato per il reso:

 int mul(int *, int i, int j) { return i*j; } std::string mul(std::string *, char c, int n) { return std::string(n, c); } 

Quale può essere usato con il codice:

 int n = mul((int *) NULL, 6, 3); // n = 18 std::string s = mul((std::string *) NULL, 54, 3); // s = "666" 

Differenzia in modo esplicito le chiamate definendo il valore di ritorno

Con questa soluzione, creiamo una funzione “fittizia” con codice che non verrà compilato se istanziato:

 template T mul(int i, int j) { // If you get a compile error, it's because you did not use // one of the authorized template specializations const int k = 25 ; k = 36 ; } 

Noterai che questa funzione non verrà compilata, il che è una buona cosa perché vogliamo utilizzare solo alcune funzioni limitate attraverso la specializzazione del modello:

 template<> int mul(int i, int j) { return i * j ; } template<> std::string mul(int i, int j) { return std::string(j, static_cast(i)) ; } 

Quindi, il seguente codice verrà compilato:

 int n = mul(6, 3); // n = 18 std::string s = mul(54, 3); // s = "666" 

Ma questo non lo farà:

 short n2 = mul(6, 3); // error: assignment of read-only variable 'k' 

Differenzia in modo esplicito le chiamate definendo il valore restituito, 2

Ehi, hai imbrogliato anche tu!

Giusto, ho usato gli stessi parametri per le due funzioni “sovraccariche”. Ma hai iniziato a barare (vedi sopra) …

^ _ ^

Più seriamente, se hai bisogno di avere parametri diversi, allora dovrai scrivere più codice, e poi usare esplicitamente i tipi giusti quando chiami le funzioni per evitare ambiguità:

 // For "int, int" calls template T mul(int i, int j) { // If you get a compile error, it's because you did not use // one of the authorized template specializations const int k = 25 ; k = 36 ; } template<> int mul(int i, int j) { return i * j ; } // For "char, int" calls template T mul(char i, int j) { // If you get a compile error, it's because you did not use // one of the authorized template specializations const int k = 25 ; k = 36 ; } template<> std::string mul(char i, int j) { return std::string(j, (char) i) ; } 

E questo codice sarebbe usato come tale:

 int n = mul(6, 3); // n = 18 std::string s = mul('6', 3); // s = "666" 

E la seguente riga:

 short n2 = mul(6, 3); // n = 18 

Non vorrei ancora compilare.

Conclusione

Adoro C ++ …

:-p

Se volessi rendere mul una vera funzione invece di una class, potresti semplicemente usare una class intermedia:

 class StringOrInt { public: StringOrInt(int p1, int p2) { param1 = p1; param2 = p2; } operator int () { return param1 * param2; } operator std::string () { return std::string(param2, param1 + '0'); } private: int param1; int param2; }; StringOrInt mul(int p1, int p2) { return StringOrInt(p1, p2); } 

Questo ti permette di fare cose come passare mul come una funzione in algoritmi std:

 int main(int argc, char* argv[]) { vector x; x.push_back(3); x.push_back(4); x.push_back(5); x.push_back(6); vector intDest(x.size()); transform(x.begin(), x.end(), intDest.begin(), bind1st(ptr_fun(&mul), 5)); // print 15 20 25 30 for (vector::const_iterator i = intDest.begin(); i != intDest.end(); ++i) cout << *i << " "; cout << endl; vector stringDest(x.size()); transform(x.begin(), x.end(), stringDest.begin(), bind1st(ptr_fun(&mul), 5)); // print 555 5555 55555 555555 for (vector::const_iterator i = stringDest.begin(); i != stringDest.end(); ++i) cout << *i << " "; cout << endl; return 0; } 

No.

Non puoi sovraccaricare con il valore di ritorno perché il chiamante può fare qualsiasi cosa (o niente) con esso. Prendere in considerazione:

mul(1, 2);

Il valore restituito viene semplicemente gettato via, quindi non c’è modo di scegliere un sovraccarico basato sul solo valore di ritorno.

Utilizza la conversione implicita in una class intermedia.

 class BadIdea { public: operator string() { return "silly"; } operator int() { return 15; } }; BadIdea mul(int, int) 

Hai un’idea, però, un’idea terribile.

Sia mul una class, mul (x, y) il suo costruttore e sovraccarichi alcuni operatori di casting.

Non è ansible sovraccaricare una funzione solo in base al valore di ritorno.

Tuttavia, mentre in senso stretto non si tratta di una funzione sovraccaricata, è ansible restituire dalla funzione come un’istanza di una class che sovraccarica gli operatori di conversione.

Presumo che tu possa averlo restituire qualche strano tipo Foo che cattura solo i parametri e quindi Foo ha un operatore implicito int e una stringa di operatore, e sarebbe “funzionante”, anche se non sarebbe un sovraccarico, piuttosto un trucco implicito di conversione.

Hmmm, il seguente articolo del progetto di codice sembra fare ciò che stai cercando. Deve essere magico;)

Breve e semplice, la risposta è NO. In C ++ i requisiti sono:

1: il nome delle funzioni DEVE essere lo stesso
2: l’insieme degli argomenti DEVE differire
* Il tipo di reso può essere uguale o diverso

 //This is not valid int foo(); float foo(); typedef int Int; int foo(int j); int foo(Int j); //Valid: int foo(int j); char* foo(char * s); int foo(int j, int k); float foo(int j, float k); float foo(float j, float k); 

Per quanto ne so, non puoi (grande peccato, però …). Come soluzione alternativa, puoi definire un parametro ‘out’ invece di sovraccaricarlo.

Non in C ++. Quello che otterresti nell’esempio sopra sarebbe il valore restituito che è un cast di int in qualcosa che una string può capire, molto probabilmente un char . Quale sarebbe ASCII 18 o “dispositivo di controllo 2”.

Puoi usare la soluzione del functor qui sopra. C ++ non supporta questo per le funzioni ad eccezione di const. È ansible sovraccaricare basato su const.

Potresti usare un modello, ma dovresti specificare il parametro del modello quando effettui la chiamata.

Mettilo in un namespace diverso? Quello sarebbe come lo farei. Non strettamente un sovraccarico, piuttosto un semplice avere due metodi con lo stesso nome, ma un diverso ambito (da cui l’operatore di risoluzione :: scope).

Quindi stringnamespace :: mul e intnamespace :: mul. Forse non è proprio quello che stai chiedendo, ma sembra l’unico modo per farlo.

Potresti fare qualcosa del genere

 template T mul(int i,int j){ return i * j; } template<> std::string mul(int i,int j){ return std::string(j,i); } 

E poi chiamalo così:

 int x = mul(2,3); std::string s = mul(2,3); 

Non c’è modo di sovraccaricare il valore di ritorno.

OK genio;) questo è come lo fai come un professionista.

 class mul { int m_i,m_j; public: mull(int i,int j):m_i(i),m_j(j){} template operator R() { return (R)m_i * m_j; } };
class mul { int m_i,m_j; public: mull(int i,int j):m_i(i),m_j(j){} template operator R() { return (R)m_i * m_j; } }; 

usare come

 double d = mul(1,2); long l = mul(1,2); 

no stupido <>