2010-05-12 21 views
13

estaba aburrido y se acercó con tal Hack (pseudocódigo):C++ sobrecarga de tipo de retorno hackear

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(...); 

es malo para su uso en código real?

mi posible escenario de uso es, por ejemplo, producto de elementos de la matriz, que puede/no puede desbordamiento:

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

La razón de la función, en caso de usar plantillas, de los parámetros de plantilla se pueden deducir de los argumentos de funciones

+3

No veo el problema si se trata de un "problema real". ¿Cuál es el propósito de la función de todos modos, me parece innecesario. – piotr

+0

bien, hace que la función se comporte de forma "antinatural". Me preocupa si hay algún elemento de sorpresa. – Anycorn

+1

No creo que sea tan confuso; hay idiomas en los que las funciones pueden (naturalmente) cambiar su comportamiento en función del tipo de retorno esperado, o (para idiomas con múltiples valores de retorno) el número de valores esperados –

Respuesta

5

prefiero usar especialización de plantilla, sólo se siente menos "hacky" y probablemente será más rápido (sin la creación de objetos, aunque por supuesto que puede ser optimizado por compilador inteligente).

Pero de todos modos, prefiero ver el código como

template<typename T> T function(); 

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

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

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

No hay nada malo con su código sin embargo, especialmente si se queda lejos de los tipos numéricos/punteros pues de lo contrario podrían producirse efectos imprevistos, las reglas de conversión es bastante complicado en C++.

1

No, y no es un truco. Es el punto de la sobrecarga del operador. Siempre que sus sobrecargas tengan un propósito, ¿por qué no?

4

El problema es que si la función tiene dos tipos de devolución, probablemente esté haciendo dos cosas diferentes (alternativas). Y en la medida de lo posible, cada función/método debería hacer una cosa coherente.

0

En su ejemplo, usted permite los moldes a int y float. Siempre que estos dos moldes realicen la misma lógica básica, es decir, operator int() { return operator float(); }, no veo ningún problema. Si se comportan de manera diferente, esto definitivamente podría dar lugar a algunas sorpresas o ambigüedades. Esto es porque esperaría que los resultados numéricos tengan un significado coherente.

0

Si usted realmente quiere decir algo como esto:

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(...); 

Entonces sí, creo que está bien y me gustaría contar con él como un truco.

Si funciona. No lo probé en absoluto.

+1

Parece que su código, 'function' hace todo el trabajo, y luego devuelve dos posibles resultados, que' proxy' luego selecciona de acuerdo con el operador de conversión deseado. Mi impresión del código de Aaa era que 'función' no haría más que enviar los argumentos a' proxy', y el trabajo real ocurriría en los operadores de conversión para generar un solo resultado de acuerdo con el tipo deseado. –

+0

bueno, estaba pensando más en las líneas de ocultación de las funciones reales detrás de los operadores de reparto. Creo que Rob acaba de explicar lo que quise decir hace un momento – Anycorn

+0

@aaa: si 'proxy' hace el trabajo, el nombre' proxy' es engañoso. Y esa es la única pista que di sobre lo que está pasando. – Potatoswatter

3

La llamada de la función "función" se convirtió en un tipo de contexto sensible. Supongo que este truco puede ser explotado para soportar subject-oriented programming.

La programación orientada a objetos se basa en la observación de que las propiedades de un objeto no son inherentes al objeto en sí, sino que dependen de quién perciba ese objeto. Por ejemplo, desde el punto de vista del humano, el árbol no es alimento, pero desde el punto de vista de la termita, el árbol es alimento. El paradigma orientado a objetos no admite esta observación directamente, y las personas a menudo recurren a diseños complejos no naturales, porque intentan incorporar todas las vistas subjetivas diferentes de un objeto en una entidad ("clase"), siguiendo las pautas OOP sin pensar.

Por lo tanto, intentemos expresar las percepciones subjetivas explícitamente, utilizando el truco en cuestión para obtener la sensibilidad del contexto.

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

Tenga en cuenta que el árbol no sabe lo que se come.

(gran cuestión de hecho, me hizo reflexionar sobre ella mucho)

+0

gracias. lecturA INTERESANTE – Anycorn

1

En realidad, parece que ha reinventado un Variant type. Solo eche un vistazo a todos los * tipos de variantes que están presentes en muchos marcos, por ejemplo .: MS usa VARIANT, Qt tiene QVariant.