Я скучал и придумал такой взлом (псевдокод):
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(...);
действительно ли это является злым для использования в реальном коде?
мой возможный сценарий использования является, например, продуктом элементов массива, которые не могут переполниться:
int size(short *array);
short size(short *array);
Причина функции, в случае, если Вы используете шаблоны, чем шаблонные параметры может быть выведена из аргументов функции
Я бы предпочел использовать специализацию шаблона, просто кажется менее "хакерским" и, вероятно, будет быстрее (без создания объектов, хотя, конечно, это можно оптимизировать с помощью интеллектуального компилятора).
Но в любом случае, я бы предпочел увидеть код вроде
template<typename T> T function();
template<> int function() {
return 1;
}
template<> float function() {
return 1.0;
}
....
int a = function<int>();
float b = function<float>();
Хотя в вашем коде нет ничего плохого, особенно если вы держитесь подальше от числовых типов / указателей, поскольку в противном случае могут возникнуть непредвиденные эффекты, правила преобразования довольно сложны в C ++.
Проблема в том, что если функция имеет два возвращаемых типа, она, вероятно, выполняет два разных (альтернативных ) вещи . И, насколько это возможно, каждая функция / метод должна выполнять одно связное действие.
В вашем примере вы разрешаете приведение типов к int
и float
. Пока эти два преобразования выполняют одну и ту же базовую логику, то есть operator int () {return operator float (); }
Не вижу проблем. Если они будут вести себя по-другому, это определенно может привести к некоторым сюрпризам или двусмысленностям. Это потому, что я ожидал, что числовые результаты будут иметь логичное значение.
Нет, и это не взлом. В этом весь смысл перегрузки оператора. Если ваши перегрузки служат определенной цели, почему бы и нет?
Если вы действительно имеете в виду что-то вроде этого:
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(...);
Тогда да, я думаю, это круто, и я бы посчитал это хаком.
Если это работает. Я не тестировал это вообще.
На самом деле кажется, что вы заново изобрели Тип варианта . Просто взгляните на все * типы вариантов, которые присутствуют во многих фреймворках, например: MS использует VARIANT , Qt имеет QVariant .
Вызов функции "function" стал как бы контекстно чувствительным. Я полагаю, что этот трюк можно использовать для поддержки предметно-ориентированного программирования.
Субъектно-ориентированное программирование основано на наблюдении, что свойства объекта не присущи самому объекту, а зависят от того, кто воспринимает этот объект. Например, с точки зрения человека, дерево не является пищей, но с точки зрения термита, дерево является пищей. Объектно-ориентированная парадигма не поддерживает это наблюдение напрямую, и люди часто приходят к сложным неестественным конструкциям, потому что они пытаются включить все различные субъективные взгляды на объект в одну сущность ("класс"), бездумно следуя рекомендациям ООП.
Итак, давайте попробуем изложить субъективные представления в явном виде, используя рассматриваемый трюк для получения контекстной чувствительности.
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();
Заметим, что дерево не знает, что его ест.
(отличный вопрос, заставивший меня задуматься над ним)