58
задан 31 March 2019 в 19:33

12 ответов

Редактирование: , В то время как я полностью ответил на вопрос, как спросили, взгляните на 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;

знать, тем не менее, что существует все еще вызов функции (неявный оператора броска), даже при том, что это не видимо. И на самом деле оператор делает некоторые истинные вычисления (вместо того, чтобы просто получить доступ к внутреннему участнику), таким образом, сомнительно, если скрытие вызова функции действительно является хорошей идеей...

57
ответ дан 1 November 2019 в 13:06

Можно быть рядом с этим с при помощи лямбды в 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;
};
50
ответ дан 1 November 2019 в 13:06

Самое близкое, которое, вероятно, можно получить, должно создать функтор:

#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";
    }
}
25
ответ дан 1 November 2019 в 13:06

Существует два главных метода:

  1. Задержанное вычисление - вместо z являющийся простой переменной, сделайте это функцией, которая вычисляет значение по требованию (см. другие ответы для примеров). Это может быть исходным кодом, прозрачным, если z некоторый объект прокси с неявным преобразованием в необходимый тип (как в ответ Аконкагуа ).

  2. Явное уведомление об изменениях. Это требует x и y быть заметными типами; когда любое значение изменений, затем z обновляет себя (и уведомляет его наблюдателей если применимо).

первая версия обычно предпочитается, но второе может быть более соответствующим, если Вам нужно z, чтобы быть заметным типом.

20
ответ дан 1 November 2019 в 13:06

Это походит проблема 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 членская переменная, таким образом, Вы не можете случайно использовать его вместо того, чтобы вызвать функцию.

17
ответ дан 1 November 2019 в 13:06
  1. Вы создаете функцию для этого.
  2. Вы вызываете функцию с соответствующими аргументами при необходимости в значении.

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);
8
ответ дан 1 November 2019 в 13:06

Можно определить следующую лямбду 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
}
5
ответ дан 1 November 2019 в 13:06

Таким образом, большая проблема, которую я вижу с предоставленными решениями для лямбды, состоит в том, что 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();
5
ответ дан 1 November 2019 в 13:06

Вы можете получать то, что Вы просите при помощи макросов:

{
    int x, y;
#define z (x + y)
    /* use x, y, z */
#undef z
}

Эти #undef для небольшой исправности. Для большей исправности не используйте макросы вообще, и идите с одним из других ответов и имейте дело с дополнительным многословием.

, Хотя класс с пользовательским operator int работал бы в большом количестве случаев... хм.

3
ответ дан 1 November 2019 в 13:06

То, что Вы описываете, позднее связывание , который скомпилированный язык как C++ может сделать только с трудностью. На интерпретируемом языке все, в чем Вы нуждаетесь, является способностью установить z на неоцененное выражение и привязку задержки значения z, пока вычисление не необходимо, обычно сообщается вызовом функции, которая вызывает оценку, такую как оценка в Lisp. На языке правил моей Экспертной системы у меня нет только оценки, но и noeval, который защищает его аргумент от одного уровня оценки. Это обеспечивает детализированное управление привязкой, при этом некоторые подвыражения оценены (связанные) и другие не при желании. Это не применимо к Вашему сценарию, но он задает тон с точки зрения среды языка.

3
ответ дан 1 November 2019 в 13:06

Вы могли записать класс, который инкапсулирует его состояние для обновления или при видоизменении или возвратите правильный результат при необходимости:

#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 являются ненужными.

3
ответ дан 1 November 2019 в 13:06

Хорошо, позвольте мне наконец записать право и только истинный ответ на Ваш установленный вопрос:

Вы не можете.

Вы не можете записать z = x + y и затем иметь весь код с помощью z волшебно повторно выполненный каждый раз, когда x или y изменения.

Поэтому, что может быть сделано?

, Как упомянуто в других ответах, существует несколько шаблонов для выражения, что Вы хотите, чтобы изменения X и Y вызвали некоторые обновления, но в любом случае Вам нужны эти обновления для случая более или менее явно.

В зависимости от варианта использования, Вы можете:

  • Повторно вычислили значение так или иначе в любом случае, это имеет значение. , Например, если Вы пишете игру и перерисовываете экран каждый кадр, затем вероятно, просто удостоверившись, что Вы случайно не сохраняете значение z между кадрами, достаточно. Знайте, когда Ваше значение может измениться и когда оно не может. Используете ли Вы функцию, лямбду, метод класса, или просто повторяете выражение, главным образом эстетическое решение. При наличии это - лучший подход, потому что это полностью прозрачно.

    , Например, в мчащейся игре Вы, вероятно, обновили бы свою текущую скорость в начале нового вычисления галочки и затем использовали бы обновленное значение при вычислениях перемещения автомобиля, при перерисовке индикатора скорости, при создании размытости изображения движущегося объекта, и так далее. Вам не нужны никакое волшебство и даже функция, можно просто использовать переменную, потому что Вы знаете, что Ваша скорость не изменится во время одного кадра.

  • Вызов обновление явно. Использование он, например, когда у Вас есть единственный виджет, необходимо обновить. Оборотная сторона - то, что необходимо не забыть называть обновление, которое является несколько хрупким, но на позитивном аспекте - это очень просто. Второй план должен иметь вызов обновления, интегрированный с методом set, делая это видом реализации Наблюдателя бедного человека.

  • Шаблон "наблюдатель" Использования (см. также сигналы и слоты, это - один способ реализовать Наблюдателя). Используйте его, например, когда у Вас есть много виджетов для обновления, или Вы создаете их динамично. Избегайте использования его, когда одна из вышеупомянутых работ, они будут путь более просты.

  • Использование выделенная библиотека реактивного программирования . Материал как таковой существует, я чувствую себя обязанным упомянуть это. Однако я честно не вижу приложения, где я использовал бы его. Это главным образом походит на сложный способ стрелять в Ваши ноги. Неявные обновления собираются иметь неприятные последствия, и необходимо будет переписать все. Просто не делайте, не в C++. Что важно: в то время как этот подход является самым близким к "волшебно обновлению все", это наложило бы ограничения на то, как Вы пишете свой код, и в конечном счете Вы получите одно из вышеупомянутых решений, просто более сложных.

0
ответ дан 1 November 2019 в 13:06

Другие вопросы по тегам:

Похожие вопросы: