Редактирование: , В то время как я полностью ответил на вопрос, как спросили, взгляните на Artelius' ответ , также. Это решает некоторые проблемы, которые мой ответ не делает (инкапсуляция, предотвращение дублирований, риски повисших ссылок). Возможную оптимизацию, если вычисление является дорогим, показывают в Jonathan Mee ответ .
<час>Вы имеете в виду что-то вроде этого:
class Z
{
int& x;
int& y;
public:
Z(int& x, int& y) : x(x), y(y) { }
operator int() { return x + y; }
};
вычисление задержек класса результата до литой как интервал В литом виде оператор не является явным, Z
может использоваться каждый раз, когда интервал требуется. Как существует перегрузка operator<<
для интервала, можно использовать его с, например, std::cout
непосредственно:
int x, y;
Z z(x, y);
std::cin >> x >> y;
if(std::cin) // otherwise, IO error! (e. g. bad user input)
std::cout << z << std::endl;
знать, тем не менее, что существует все еще вызов функции (неявный оператора броска), даже при том, что это не видимо. И на самом деле оператор делает некоторые истинные вычисления (вместо того, чтобы просто получить доступ к внутреннему участнику), таким образом, сомнительно, если скрытие вызова функции действительно является хорошей идеей...
Можно быть рядом с этим с при помощи лямбды в C++. Обычно то, когда Вы устанавливаете переменную как
int x;
int y;
int z{x + y};
z
, только будет результатом x + y
в то время. Необходимо было бы сделать z = x + y;
каждый раз, когда Вы изменяетесь x
или y
для хранения этого обновлением.
при использовании лямбды, хотя, у Вас может быть она получение, что возражает, это должно относиться к, и какое вычисление должно быть сделано, и затем каждый раз, когда Вы получаете доступ к лямбде, она даст Вам результат в то время. Это похоже
int x;
int y;
auto z = [&](){ return x + y; };
cin >> x;
cin >> y;
cout << z();
, и теперь z()
будет иметь правильное значение вместо неинициализированного мусора, который имел исходный код.
, Если вычисление является очень дорогим, можно даже добавить некоторое кэширование к лямбде, чтобы удостовериться, что Вы не выполняете вычисление, когда Вы не должны. Это было бы похоже
auto z = [&](){ static auto cache_x = x;
static auto cache_y = y;
static auto cache_result = x + y;
if (x != cache_x || y != cache_y)
{
cache_x = x;
cache_y = y;
cache_result = x + y;
}
return cache_result;
};
Самое близкое, которое, вероятно, можно получить, должно создать функтор:
#include <iostream>
int main() {
int x;
int y;
auto z = [&x, &y] { return x + y; }; // a lambda capturing x and y
while(true) {
std::cin >> x;
std::cin >> y;
std::cout << z() << "\n";
}
}
Существует два главных метода:
Задержанное вычисление - вместо z
являющийся простой переменной, сделайте это функцией, которая вычисляет значение по требованию (см. другие ответы для примеров). Это может быть исходным кодом, прозрачным, если z
некоторый объект прокси с неявным преобразованием в необходимый тип (как в ответ Аконкагуа ).
Явное уведомление об изменениях. Это требует x
и y
быть заметными типами; когда любое значение изменений, затем z
обновляет себя (и уведомляет его наблюдателей если применимо).
первая версия обычно предпочитается, но второе может быть более соответствующим, если Вам нужно z
, чтобы быть заметным типом.
Это походит проблема XY (предназначенная игра слов).
От звука его, Вы не действительно написание кода согласно хорошим объектно-ориентированным методам. Я советовал бы Вам не использовать "приемы", которые другие люди предложили, но на самом деле изучить, как лучше использовать структуру OO.
, Прежде чем я войду в это, обратите внимание, что присвоение отлично от отношение равенства . =
в C++ присвоение, которое не является тем же как =
в математике. Существуют некоторые (но не многие) языки программирования, которые действительно поддерживают отношения равенства, но C++ не является одним из них. Вещь, добавляя, что поддержка отношений равенства представляет "кучу" новых проблем, таким образом, это не столь просто как, "почему это еще не находится в C++".
Так или иначе, в этом случае, необходимо, вероятно, инкапсулировать связанные переменные в классе. Затем можно использовать методы для получения "актуальной" информации. Например:
class Player {
std::vector<int> inventory;
int cash;
public:
int inventory_total();
int net_worth();
}
//adds up total value of inventory
int Player::inventory_total() {
int total = 0;
for(std::vector<int>::iterator it = inventory.begin(); it != inventory.end(); ++it) {
total += *it;
}
return total;
}
//calculates net worth
int Player::net_worth() {
//we are using inventory_total() as if it were a variable that automatically
//holds the sum of the inventory values
return inventory_total() + cash;
}
...
//we are using net_worth() as if it were a variable that automatically
//holds the sum of the cash and total holdings
std::cout << player1.net_worth();
я признаю, что добавление этого поведения к классу вполне немного более сложно, чем высказывание z = x + y
, но это действительно - только несколько дополнительных строк кода.
, Который был бы очень раздражающим и подверженным ошибкам, если бы Вы забыли вызывать функцию где-нибудь.
В этом случае объект не делает , имеют net_worth
членская переменная, таким образом, Вы не можете случайно использовать его вместо того, чтобы вызвать функцию.
int z(int x, int y)
{
return (x + y);
}
int x;
int y;
// This does ot work
// int z{x + y};
cin >> x;
cin >> y;
cout << z(x, y);
Можно определить следующую лямбду z
, который всегда возвращает текущее значение x+y
, потому что x
и y
получены ссылкой:
int main()
{
int x;
int y;
const auto z = [&x, &y](){ return x+y; };
std::cin >> x; // 1
std::cin >> y; // 2
std::cout << z() << std::endl; // 3
std::cin >> x; // 3
std::cin >> y; // 4
std::cout << z() << std::endl; // 7
}
Таким образом, большая проблема, которую я вижу с предоставленными решениями для лямбды, состоит в том, что z
вычисляется каждый раз, когда это осмотрено , даже если ни x
, ни y
изменился . Для обхождения этого, действительно необходимо связать эти переменные. Я предложил бы делать это через class
:
class foo {
int x;
int y;
int z;
void calculate() { z = (x + y) / 2; }
friend istream& operator >>(istream& lhs, foo& rhs);
public:
void set_x(const int param) {
x = param;
calculate();
}
int get_x() const { return x; }
void set_y(const int param) {
y = param;
calculate();
}
int get_y() const { return y; }
int get_z() const { return z; }
};
istream& operator >>(istream& lhs, foo& rhs) {
lhs >> rhs.x >> rhs.y;
rhs.calculate();
return lhs;
}
Это повторно вычислит z
, каждый раз x
или y
установлен. Это - хорошее решение, если Вы получаете доступ z
часто, и x
, и y
нечасто устанавливаются. Если x
и y
часто устанавливаются, или calculate
является дорогим, Вы могли бы рассмотреть:
class foo {
int x;
int y;
int z;
bool dirty;
void calculate() { z = (x + y) / 2; }
friend istream& operator >>(istream& lhs, foo& rhs);
public:
void set_x(const int param) {
x = param;
dirty = true;
}
int get_x() const { return x; }
void set_y(const int param) {
y = param;
dirty = true;
}
int get_y() const { return y; }
int get_z() const {
if(dirty) {
calculate();
}
return z;
}
};
istream& operator >>(istream& lhs, foo& rhs) {
lhs >> rhs.x >> rhs.y;
rhs.dirty = true;
return lhs;
}
Примечание, что я включал оператор извлечения, поэтому какой бы ни Вы выбираете свой код, может превратиться во что-то столь же простое как:
foo xyz;
cin >> xyz;
cout << xyz.get_z();
Вы можете получать то, что Вы просите при помощи макросов:
{
int x, y;
#define z (x + y)
/* use x, y, z */
#undef z
}
Эти #undef
для небольшой исправности. Для большей исправности не используйте макросы вообще, и идите с одним из других ответов и имейте дело с дополнительным многословием.
, Хотя класс с пользовательским operator int
работал бы в большом количестве случаев... хм.
То, что Вы описываете, позднее связывание , который скомпилированный язык как C++ может сделать только с трудностью. На интерпретируемом языке все, в чем Вы нуждаетесь, является способностью установить z на неоцененное выражение и привязку задержки значения z, пока вычисление не необходимо, обычно сообщается вызовом функции, которая вызывает оценку, такую как оценка в Lisp. На языке правил моей Экспертной системы у меня нет только оценки, но и noeval, который защищает его аргумент от одного уровня оценки. Это обеспечивает детализированное управление привязкой, при этом некоторые подвыражения оценены (связанные) и другие не при желании. Это не применимо к Вашему сценарию, но он задает тон с точки зрения среды языка.
Вы могли записать класс, который инкапсулирует его состояние для обновления или при видоизменении или возвратите правильный результат при необходимости:
#include <iostream>
template<typename T, typename U, typename V>
class DynamicCalc
{
public:
DynamicCalc(const T& func, const U& memberOne, const V& memberTwo) :
_func(func)
, _memberOne(memberOne)
, _memberTwo(memberTwo)
{
}
void SetMemberOne(const U& memberOne) { _memberOne = memberOne; }
void SetMemberTwo(const U& memberTwo) { _memberTwo = memberTwo; }
auto Retrieve() { return _func(_memberOne, _memberTwo); }
U GetMemberOne() { return _memberOne; }
V GetMemberTwo() { return _memberTwo; }
private:
T _func;
U _memberOne;
V _memberTwo;
};
int main() {
auto func = [](int x, int y) {
return x + y;
};
DynamicCalc<decltype(func), int, int> c(func, 3, 5);
c.SetMemberOne(5);
std::cout << c.Retrieve();
}
, По правде говоря, если Вы рады за вычисление произойти, когда значение является reuqested затем, методы считывания/методы set являются ненужными.
Хорошо, позвольте мне наконец записать право и только истинный ответ на Ваш установленный вопрос:
Вы не можете записать z = x + y
и затем иметь весь код с помощью z
волшебно повторно выполненный каждый раз, когда x
или y
изменения.
, Как упомянуто в других ответах, существует несколько шаблонов для выражения, что Вы хотите, чтобы изменения X и Y вызвали некоторые обновления, но в любом случае Вам нужны эти обновления для случая более или менее явно.
В зависимости от варианта использования, Вы можете:
Повторно вычислили значение так или иначе в любом случае, это имеет значение. , Например, если Вы пишете игру и перерисовываете экран каждый кадр, затем вероятно, просто удостоверившись, что Вы случайно не сохраняете значение z между кадрами, достаточно. Знайте, когда Ваше значение может измениться и когда оно не может. Используете ли Вы функцию, лямбду, метод класса, или просто повторяете выражение, главным образом эстетическое решение. При наличии это - лучший подход, потому что это полностью прозрачно.
, Например, в мчащейся игре Вы, вероятно, обновили бы свою текущую скорость в начале нового вычисления галочки и затем использовали бы обновленное значение при вычислениях перемещения автомобиля, при перерисовке индикатора скорости, при создании размытости изображения движущегося объекта, и так далее. Вам не нужны никакое волшебство и даже функция, можно просто использовать переменную, потому что Вы знаете, что Ваша скорость не изменится во время одного кадра.
Вызов обновление явно. Использование он, например, когда у Вас есть единственный виджет, необходимо обновить. Оборотная сторона - то, что необходимо не забыть называть обновление, которое является несколько хрупким, но на позитивном аспекте - это очень просто. Второй план должен иметь вызов обновления, интегрированный с методом set, делая это видом реализации Наблюдателя бедного человека.
Шаблон "наблюдатель" Использования (см. также сигналы и слоты, это - один способ реализовать Наблюдателя). Используйте его, например, когда у Вас есть много виджетов для обновления, или Вы создаете их динамично. Избегайте использования его, когда одна из вышеупомянутых работ, они будут путь более просты.
Использование выделенная библиотека реактивного программирования . Материал как таковой существует, я чувствую себя обязанным упомянуть это. Однако я честно не вижу приложения, где я использовал бы его. Это главным образом походит на сложный способ стрелять в Ваши ноги. Неявные обновления собираются иметь неприятные последствия, и необходимо будет переписать все. Просто не делайте, не в C++. Что важно: в то время как этот подход является самым близким к "волшебно обновлению все", это наложило бы ограничения на то, как Вы пишете свой код, и в конечном счете Вы получите одно из вышеупомянутых решений, просто более сложных.