Изменение одного и того же вектора в нескольких изменяемых лямбдах

#c #qt #vector #lambda

#c #qt #вектор #лямбда

Вопрос:

У меня есть две разные кнопки Qt типа QPushButton* и a vector<QString> . Каждая кнопка должна отображать окно сообщения, в котором указывается значение последнего элемента вектора, и если пользователь нажимает Да, вставляет определенное значение в вектор. Приведенный ниже код показывает, как я это делаю ( QMessageBox::information они существуют только для целей отладки). Вот соответствующая часть моего кода:

 std::vector<QString> myVector;
myVector.push_back("First value");
QObject::connect(button1,amp;QPushButton::clicked,myWidget,[myVector=move(myVector)]() mutable{
    QMessageBox::information(NULL,"",std::to_string(myVector.size()).c_str());
    if(QMessageBox::question(NULL,"","The value of the last element in the vector is "   myVector[myVector.size() - 1]   ".nDo you want to insert a new value?",QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes){
        myVector.push_back("Value inserted by button 1");
    }
});
QObject::connect(button2,amp;QPushButton::clicked,myWidget,[myVector=move(myVector)]() mutable{
    QMessageBox::information(NULL,"",std::to_string(myVector.size()).c_str());
    if(QMessageBox::question(NULL,"","The value of the last element in the vector is "   myVector[myVector.size() - 1]   ".nDo you want to insert a new value?",QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes){
        myVector.push_back("Value inserted by button 2");
    }
});
  

Я уверен, что вектор никогда не бывает пустым.

Когда я нажимаю кнопку 1, все работает так, как должно. Но когда я нажимаю кнопку 2, в окне сообщения отображается, что размер вектора равен 0, и я точно знаю, что это не так. Когда я меняю местами две QObject::connect функции, то есть ставлю одну для кнопки 2 перед кнопкой 1, поведение кнопок меняется: кнопка 2 работает нормально, а кнопка 1 считает, что вектор пуст.

Не похоже, что это правильный способ заставить два лямбда захватить тот же вектор, что и изменяемый. Почему это так? Каков правильный способ сделать это?

Комментарии:

1. Вы дважды перешли от одной и той же переменной. Подумайте о том, что это делает.

2. @NathanOliver Хорошо, но как тогда поступить правильно? Достаточно ли использовать move только в одном из лямбд, а не в другом?

3. Почему бы не сохранить ссылку на вектор в каждом лямбда-выражении, если предполагается, что они должны делиться им?

4. @NathanOliver Вы имеете в виду [amp;myVector] вместо [myVector=move(myVector)] в обоих лямбдах? Я пробовал это, и это приводит к сбою программы.

5. @DonaldDuck , вы переместили вектор в первую лямбду. поскольку std::vector поддерживается семантика перемещения, вы должны ожидать, что вектор станет пустым вне первого лямбда-выражения (после первого connect вызова).

Ответ №1:

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

Что вы можете сделать, это создать std::shared_ptr<std::vector<QString>> и записать это shared_ptr по значению в каждом из лямбд. Это гарантирует, что вектор будет иметь соответствующее время жизни и что обе кнопки работают с одним и тем же вектором. Ваш мог бы выглядеть примерно так

 std::shared_ptr<std::vector<QString>> myVector;
myVector->push_back("First value");
QObject::connect(button1,amp;QPushButton::clicked,myWidget,[=]() mutable{
    QMessageBox::information(NULL,"",std::to_string(myVector->size()).c_str());
    if(QMessageBox::question(NULL,"","The value of the last element in the vector is "   (*myVector)[myVector.size() - 1]   ".nDo you want to insert a new value?",QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes){
        myVector->push_back("Value inserted by button 1");
    }
});
QObject::connect(button2,amp;QPushButton::clicked,myWidget,[=]() mutable{
    QMessageBox::information(NULL,"",std::to_string(myVector.size()).c_str());
    if(QMessageBox::question(NULL,"","The value of the last element in the vector is "   (*myVector)[myVector->size() - 1]   ".nDo you want to insert a new value?",QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes){
        myVector->push_back("Value inserted by button 2");
    }
});
  

Также обратите внимание, что я изменил [myVector->size()] значение на [myVector->size() - 1] as vector[size()] на один конец после окончания вектора, к которому вы не можете получить доступ.

Комментарии:

1. Нужно ли мне что-либо #include , чтобы иметь возможность использовать std::shared_ptr ?

2. @DonaldDuck Да. #include <memory> это то, что вам понадобится. У Qt, вероятно, есть своя версия, если вы хотите придерживаться типов Qt. Я просто использовал std тот, который есть, ну, стандартный 🙂

3. Если я хочу использовать другую переменную в лямбде без ее изменения, например, если другая переменная есть int myVar , должен ли я это делать [myVar,=] ?

4. @DonaldDuck Если вы используете [=], он будет фиксировать все по значению, которое вы используете в теле лямбда. Если вам нужны другие варианты, смотрите Раздел Захвата лямбда здесь