#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 Если вы используете [=], он будет фиксировать все по значению, которое вы используете в теле лямбда. Если вам нужны другие варианты, смотрите Раздел Захвата лямбда здесь