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