2010-05-12 14 views
13

Ich war langweilig und kam mit einem solchen Hack (Pseudo-Code):C++ Rückgabetyp Überlastung hackt

1 struct proxy { 
2  operator int(); // int function 
3  operator double(); // double function 
4  proxy(arguments); 
5  arguments &arguments_; 
6 }; 
7 
8 proxy function(arguments &args) { 
9  return proxy(args); 
10 } 
11 int v = function(...); 
12 double u = function(...); 

ist es das Böse in echtem Code zu benutzen?

mein mögliches Verwendungsszenario ist beispielsweise Produkt der Array-Elemente, die kann/darf nicht über:

int size(short *array); 
short size(short *array); 

Der Grund für die Funktion, falls Sie Vorlagen verwenden, als Template-Parameter können vom Funktionsargument abgeleitet werden

+3

Ich sehe das Problem nicht, wenn es ein "echtes Problem" adressiert. Was ist der Zweck der Funktion sowieso, scheint mir unnötig. – piotr

+0

Nun, es macht Funktion "unnatürlich" Art von verhalten. Ich bin besorgt, wenn es ein Element der Überraschung gibt. – Anycorn

+1

Ich glaube nicht, dass es so verwirrend wäre; Es gibt Sprachen, in denen Funktionen (natürlich) ihr Verhalten basierend auf dem erwarteten Rückgabetyp ändern können, oder (für Sprachen mit mehreren Rückgabewerten) hat die Anzahl der erwarteten Werte –

Antwort

5

ich eher Template-Spezialisierung verwenden würde, fühlt sich einfach weniger „Hacky“ und wahrscheinlich wird schneller sein (keine Objekterstellung, obwohl natürlich das sein kann optimiert durch intelligenten Compiler).

Aber wie auch immer, würde ich eher Code wie

template<typename T> T function(); 

template<> int function() { 
    return 1; 
} 

template<> float function() { 
    return 1.0; 
} 

.... 
int a = function<int>(); 
float b = function<float>(); 

Nichts falsch mit Ihrem Code sieht vor allem aber, wenn Sie bleiben weg von numerischen Typen/Zeiger da sonst unerwartete Effekte auftreten können, die Regeln der Umwandlung ist ziemlich kompliziert in C++.

1

Nein, und es ist kein Hack. Es ist der springende Punkt beim Überladen von Operatoren. Solange Ihre Überlastungen einen Zweck erfüllen, warum nicht?

4

Das Problem ist, dass, wenn die Funktion zwei Rückgabetypen hat, es wahrscheinlich zwei verschiedene (alternative) Dinge tut. Und soweit möglich, sollte jede Funktion/Methode eine zusammenhängende Sache machen.

0

In Ihrem Beispiel erlauben Sie Umwandlungen zu int und float. Solange diese beiden Umwandlungen die gleiche Grundlogik ausführen, d.h. operator int() { return operator float(); }, sehe ich kein Problem. Wenn sie sich anders verhalten, kann dies durchaus zu Überraschungen oder Unklarheiten führen. Dies liegt daran, dass ich erwarte, dass numerische Ergebnisse eine kohärente Bedeutung haben.

0

Wenn Sie wirklich so etwas wie dies bedeuten:

1 struct proxy { 
2  operator long() { return refs.first; } // long has greater precision 
3  operator double() { return refs.second; } // double has greater range 
4  proxy(long const &, double const &); 
5  pair< long const &, double const & > refs; 
6 }; 
7 
8 proxy function() { 
9  return proxy(numeric_limits<long>::max() + 1, 
        double(numeric_limits<long>::max())); 
10 } 
11 int v = function(...); 
12 double u = function(...); 

Dann ja, ich denke, das ist cool und ich würde es als ein Hack zählen.

Wenn es funktioniert. Ich habe es überhaupt nicht getestet.

+1

Es sieht aus wie in Ihr Code, 'function', erledigt die ganze Arbeit und gibt dann zwei mögliche Ergebnisse zurück, die dann von' proxy' basierend auf dem gewünschten Konvertierungsoperator ausgewählt werden. Mein Eindruck von Aaas Code war, dass 'function' nichts anderes tun würde, als die Argumente an 'proxy' weiterzuleiten, und die wirkliche Arbeit würde in den Konvertierungsoperatoren auftreten, um nur ein Ergebnis entsprechend dem gewünschten Typ zu erzeugen. –

+0

Nun, ich dachte mehr über die Zeilen aus, in denen die eigentlichen Funktionen hinter den Darstellern versteckt waren. Ich denke Rob hat gerade erklärt, was ich vor einer Sekunde meinte: – Anycorn

+0

@aaa: Wenn 'proxy' die Arbeit macht, ist der Name' proxy' irreführend. Und das ist der einzige Hinweis darauf, was passiert ist. – Potatoswatter

3

Der Aufruf der Funktion "function" wurde kontextsensitiv. Ich nehme an, dieser Trick kann ausgenutzt werden, um subject-oriented programming zu unterstützen.

Die subjektorientierte Programmierung basiert auf der Beobachtung, dass Eigenschaften eines Objekts dem Objekt selbst nicht innewohnen, sondern davon abhängen, wer dieses Objekt wahrnimmt. Zum Beispiel ist Baum aus der Sicht des Menschen nicht Nahrung, sondern aus der Sicht der Termiten ist Baum Nahrung. Das objektorientierte Paradigma unterstützt diese Beobachtung nicht direkt, und die Menschen kommen oft zu komplexen unnatürlichen Designs, weil sie versuchen, alle verschiedenen subjektiven Ansichten eines Objekts in eine Einheit ("Klasse") einzubauen, indem sie unbedachte OOP-Richtlinien befolgen.

Versuchen wir also, subjektive Wahrnehmungen explizit zu formulieren, indem wir den betreffenden Trick verwenden, um Kontextsensitivität zu erhalten.

template<class FoodSource> 
class FoodFrom {}; 
//forward declarations 
class Tree; 
class Termite; 
class Human; 

//property "food" of a tree 
template<> 
class FoodFrom<Tree> 
{ 
public: 
    FoodFrom(Tree& _tree): tree(_tree) {} 

    //termite perception of tree as food 
    operator FoodFor<Termite>() 
    { 
     int happiness_increase = 5; 
     tree.mass -= 10; 
     return FoodFor<Termite>(happiness_increase); 
    } 
    //human perception of tree as food 
    operator FoodFor<Human>() 
    { 
     int happiness_increase = 0; 
     return FoodFor<Human>(happiness_increase); 
    } 
private: 
    Tree& tree; 
}; 
//property "food" of a termite 
template<> 
class FoodFrom<Termite> 
{ 
public: 
    FoodFrom(Termite& _termite): termite(_termite) {} 
    //human perception of termite as food 
    operator FoodFor<Human>() 
    { 
     int happiness_increase = -100; 
     //apparently, the termite ought to be terminated due to such a violent act 
     termite.~Termite(); 
     return FoodFor<Human>(happiness_increase); 
    } 
private: 
    Termite& termite; 
}; 

//simple class FoodFor, just for demonstration purposes 
class FoodBase 
{ 
public: 
    FoodBase(int _value) : value(_value) {} 
    int value; 
}; 
template<class T> 
class FoodFor: public FoodBase 
{ 
public: 
    FoodFor(): FoodBase(0) {} 
    FoodFor(int _value) : FoodBase(_value) {} 
}; 

class AliveBeing 
{ 
public: 
    AliveBeing(): happiness(100) {} 
    bool is_happy() 
    { 
     return happiness > 0; 
    } 
    void eat() 
    { 
     happiness += getMeal()->value; 
    } 
private: 
    int happiness; 
    virtual FoodBase* getMeal() = 0; 
}; 
class Tree: public AliveBeing 
{ 
public: 
    FoodFrom<Tree> getFood(); //see definition below 
    float mass; 
    //... 
private: 
    //we don't call getMeal for a tree in this demo 
    virtual FoodBase* getMeal() { return NULL; } 
}; 

class Termite: public AliveBeing 
{ 
public: 
    FoodFrom<Termite> getFood(); //see definition below 
    FoodFor<Termite> meal; 
private: 
    virtual FoodBase* getMeal() { return &meal; } 
}; 

class Human: public AliveBeing 
{ 
public: 
    FoodFor<Human> meal; 
private: 
    virtual FoodBase* getMeal() { return &meal; } 
}; 

//return proxy "FoodFrom" to "overload" return type 
FoodFrom<Tree> Tree::getFood() 
{ return FoodFrom<Tree>(*this); } 
FoodFrom<Termite> Termite::getFood() 
{ return FoodFrom<Termite>(*this); } 

//usage 
    Tree tree; 
    Termite funny_bug; 
    //funny_bug gets its perceived value of eating tree 
    funny_bug.meal = tree.getFood(); 
    funny_bug.eat(); 
    if(funny_bug.is_happy()) 
     funny_bug.goFindThirdPlace(); 

    //... 

    Human joel; 
    //joel get its perceived value of eating tree 
    joel.meal = tree.getFood(); 
    joel.eat(); 

    //... 

    if(joel.see(funny_bug)) 
    { 
     joel.meal = funny_bug.getFood(); 
     joel.eat(); 
    } 
    if(joel.is_happy()) 
     joel.writeAnotherGreatArticle(); 

Beachten Sie, dass der Baum nicht weiß, was es isst.

(große Frage in der Tat, hat mich viel darüber nachdenken)

+0

danke. Interessantes lesen – Anycorn

1

Eigentlich scheint es, Sie haben eine Variant type neu erfunden. Werfen Sie einen Blick auf alle * Variantentypen, die in vielen Frameworks vorhanden sind, zB: MS verwendet VARIANT, Qt hat QVariant.