Russian Qt Forum

Qt => Общие вопросы => Тема начата: AlphaGh0St от Март 08, 2016, 19:52



Название: Signal/Slot rvalue
Отправлено: AlphaGh0St от Март 08, 2016, 19:52
Всем привет!
После переустановки винды и установки нового QtCreator'a 3.6.0 c Qt 5.5.1, по лбу мне ударили грабли, которых я никак не ожидал.

Набросал простенький код с целью демонстрации проблемы. В чём суть:
Есть некий класс class Object;
Сигналим им по ссылке и принимаем в слоте по ссылке. Ссылка не константная, т.к. объект модифицируется в слоте.
Код:
Q_SIGNALS:
    void ObjSig(Object& obj);

private Q_SLOTS:
    void OnObjSig(Object& obj);

// подключаем
connect(this, SIGNAL(ObjSig(Object&)), this, SLOT(OnObjSig(Object&)));

// генерируем сигнал
emit ObjSig(Object(0, "test"));

Раньше этот код собирался и работал нормально. Сейчас же вылетает такой сюрприз:
Цитировать
ошибка: no matching function for call to 'MainWindow::ObjSig(Object)'
candidate is:
void MainWindow::ObjSig(Object&)
note:   no known conversion for argument 1 from 'Object' to 'Object&'
Оказывается, НЕТ такой функции, и, с какой-то радости, теперь передача идёт по значению, а ссылки вот вроде бы как, компилятор, и не заметил случайно.

Погуглив, выяснил, что теперь нужно использовать семантику перемещения, а описанный выше код уже не корректен. Ну ОК. Переделал:
Код:
Q_SIGNALS:
    void ObjSig(Object&& obj);

private Q_SLOTS:
    void OnObjSig(Object&& obj);

// подключаем
connect(this, SIGNAL(ObjSig(Object&&)), this, SLOT(OnObjSig(Object&&)));

// генерируем сигнал
emit ObjSig(Object(0, "test"));
И вот тут-то по лбу и ударили те самые грабли, набив такую шишку, на которую пол дня потратил и даже гугл ничем не помог. "Осчастливил" creator меня сей радостным сообщением:
Цитировать
// в мос_файле
case 0: _t->ObjSig((*reinterpret_cast< Object(*)>(_a[1]))); break;
case 1: _t->OnObjSig((*reinterpret_cast< Object(*)>(_a[1]))); break;

In static member function 'static void MainWindow::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)':
ошибка: cannot bind 'Object' lvalue to 'Object&&'
In file included from debug\moc_mainwindow.cpp:9:0:

initializing argument 1 of 'void MainWindow::ObjSig(Object&&)'
ошибка: cannot bind 'Object' lvalue to 'Object&&'
In file included from debug\moc_mainwindow.cpp:9:0:
initializing argument 1 of 'void MainWindow::OnObjSig(Object&&)'

Я морально истощён и уже просто не знаю, что делать, кроме как биться об стену головой.
Гугл, как частенько бывает, оказался бесполезным.

Как можно решить эту проблему?
Благодарю.


Название: Re: Signal/Slot rvalue
Отправлено: ViTech от Март 09, 2016, 14:18
Вы уверены, что в данном случае, правильно используете сигнал-слот взаимодействие? Подходит ли оно для того, чтобы изменять объект по ссылке?

Оказывается, НЕТ такой функции, и, с какой-то радости, теперь передача идёт по значению, а ссылки вот вроде бы как, компилятор, и не заметил случайно.

Может компилятор как раз и заметил, и разработчики, написав такую сигнатуру для метода, указывают, что можно в него передавать, а что нельзя. Синтаксис С++11 позволяет лучше это делать. Например, объект с сигналом ObjSig и объект со слотом OnObjSig находятся в разных потоках. Что произойдёт в этом OnObjSig, когда дело дойдёт до обращения к Object& obj по ссылке?


Название: Re: Signal/Slot rvalue
Отправлено: Igors от Март 09, 2016, 14:54
А почему не так
Код
C++ (Qt)
Object temp(0, "test");
emit ObjSig(temp);
Вообще - если передается по неконстантной ссылке, то ожидается изменение этого параметра которое, наверное, вызывающий будет использовать. А у Вас другая логика вызывающего, тогда уж объявляйте константной ссылкой


Название: Re: Signal/Slot rvalue
Отправлено: AlphaGh0St от Март 10, 2016, 01:13
Так можно, и это сработает.
Код:
Object temp(0, "test");
emit ObjSig(temp);
Но не хочу так делать исключительно из эстетических соображений. Мне однострочники больше нравятся.
Просто передаём объект по ссылке и нас уже не интересует, что с ним будет и каким модификациям он будет подвержен. Потому так спокойно передаю и не беспокоюсь.

Проблема остаётся открытой. Если кто-нибудь сталкивался с подобным и нашёл решение, поделитесь им, пожалуйста.


Название: Re: Signal/Slot rvalue
Отправлено: AlphaGh0St от Март 10, 2016, 01:18
Забыл сказать в предыдущем сообщении. Собственно, верное замечание, что в данном случае следует передавать константной ссылкой. Но если передавать константной ссылкой, разве можно будет проводить какие-то модификации над переданным объектом?


Название: Re: Signal/Slot rvalue
Отправлено: Racheengel от Март 10, 2016, 02:24
Забыл сказать в предыдущем сообщении. Собственно, верное замечание, что в данном случае следует передавать константной ссылкой. Но если передавать константной ссылкой, разве можно будет проводить какие-то модификации над переданным объектом?

const_cast еще никто не запрещал :)


Название: Re: Signal/Slot rvalue
Отправлено: Bepec от Март 10, 2016, 08:13
Вообще очень опасная ситуация возникает при таком подходе, мало того, она как ружьё, заряженное на стенке - выстрелит обязательно. Никто не знает когда и куда дойдёт сигнал, сколько получателей и какие потоки.
Я б советовал поменять архитектурку.


Название: Re: Signal/Slot rvalue
Отправлено: Авварон от Март 10, 2016, 09:50
Яб посоветовал почитать стандарт и узнать, что rvalue не приводится к неконст ссылке, а компилятор, позволяющий делать так, стоит выкинуть на помойку (да, я про студию)


Название: Re: Signal/Slot rvalue
Отправлено: Igors от Март 10, 2016, 11:47
Просто передаём объект по ссылке и нас уже не интересует, что с ним будет и каким модификациям он будет подвержен. Потому так спокойно передаю и не беспокоюсь.
А это логика передачи по значению

Проблема остаётся открытой. Если кто-нибудь сталкивался с подобным и нашёл решение, поделитесь им, пожалуйста.
Похоже Вы ищете пятый угол. Пример без всяких слот-сигналов
Код
C++ (Qt)
void Func1( Object & obj );
..
Func1(Object());  // error: non-const reference initialized to temporary
Т.е. так нельзя по правилам языка
Код
C++ (Qt)
void Func2( const Object & obj );
..
Func2(Object());  // а теперь так можно
Object obj;
Func2(obj);  // и так можно
 
Как известно смысл передачи по константой ссылке двоякий - может быть временный объект или нет. Ну и выберите то что соответствует логике Вашей задачи. А то вызываемый видит одно, вызывающий другое - зачем пытаться этого достичь? Если нужны оба варианта - заведите 2 сигнала, это правильно.


Название: Re: Signal/Slot rvalue
Отправлено: ViTech от Март 10, 2016, 12:21
Но не хочу так делать исключительно из эстетических соображений. Мне однострочники больше нравятся.
Просто передаём объект по ссылке и нас уже не интересует, что с ним будет и каким модификациям он будет подвержен. Потому так спокойно передаю и не беспокоюсь.
Подозреваю, что с таким спокойствием вам ещё много грабель предстоит встретить :).

Цитата: AlphaGh0St
Собственно, верное замечание, что в данном случае следует передавать константной ссылкой. Но если передавать константной ссылкой, разве можно будет проводить какие-то модификации над переданным объектом?

Можно поинтересоваться, зачем в слоте модифицировать объект, переданный из сигнала вообще, и по ссылке в частности? И ещё раз предлагаю подумать. Допустим, всё собралось так, как вы хотите и объект передаётся по ссылке. Пусть всё даже происходит в одном потоке. Если мы соединим сигнал с двумя слотами, первый слот модифицирует объект, то что получит второй слот: исходный объект из сигнала или с модификациями из первого слота?


Название: Re: Signal/Slot rvalue
Отправлено: kambala от Март 10, 2016, 12:28
как-то бессмысленно модифицировать временный объект, созданный в однострочнике, разве нет?


Название: Re: Signal/Slot rvalue
Отправлено: Racheengel от Март 10, 2016, 13:02
Если основная идея - модифицировать объект в слоте... то надо быть уверенным, что это единственный слот, связанный с данным сигналом. Иначе последствия могут быть не очень предсказуемы :)
Ну и используйте поинтер, в самом деле.