Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>У него две реализации — t_errors_without_limits и t_errors_with_limits
КД>Есть два метода, возвращающие экземпляры этих реализацией: КД>
КД>Все компилируется (VC++) и работает как и ожидается.
КД>При включенном 4-ом уровне предупреждений, компилятор предупреждает, что нельзя передавать по ссылке результат метода.
errs внутри add_current_errors изменяется? если нет, поставь const.
какой другой компилятор тебя и за взятие адреса rvalue отругает, так что не решение.
Re: Передача объекта, возвращаемого функцией, в другую функц
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Есть интерфейс t_errors. КД>У него две реализации — t_errors_without_limits и t_errors_with_limits КД>Есть два метода, возвращающие экземпляры этих реализацией: КД>Теперь есть утилита, работающая с t_errors:
КД>
КД>void add_current_errors(t_errors& errs); //errs внутри не запоминается.
КД>
КД>Все компилируется (VC++) и работает как и ожидается. КД>При включенном 4-ом уровне предупреждений, компилятор предупреждает, что нельзя передавать по ссылке результат метода. КД>Когда включаешь режим 'совместимости/соответствия' (Conformance Mode=Yes), компилятор отказывается компилировать.
Да добавь просто перегрузку для rvalue ссылки и всех делов:
void add_current_errors(t_errors& errs); //errs внутри не запоминается.void add_current_errors(t_errors&& errs) { add_current_errors(errs); }
--
Не можешь достичь желаемого — пожелай достигнутого.
КД>>Нет, const тут не нужен, потому что в errs добавляются новые объекты (описания ошибок). КД>>t_errors — это интерфейс формируемой коллекции ошибок.
I>Передавай тогда по значению, и меняй внутри как хочешь. I>void add_current_errors(t_errors errs);
Ну и будет срезка типа. А если t_errors — чисто абстрактный класс, то и вовсе ошибка компиляции.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[5]: Передача объекта, возвращаемого функцией, в другую фу
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>--- КД>Досадно, что сценарий передачи временного объекта по неконстантной ссылке есть, а официального разрешения со стороны C++ нет
Ну ОК, я могу предложить еще один способ забиндить временный объект к неконстантной ссылке. Я не хотел его показыать, поскольку он не совсем безопасен. Но, похоже, что для тебя этот способ хорошо подойдет, поскольку он вполне легален, и не требует добавления дополнительных перегрузок.
1. Определяем общую утилитную функцию make_lvalue (имя выбрать по вкусу):
Замечу, на всякий случай, T&& здесь — это так называемая форвардная ссылка, хоть по внешнему виду и выглядит как rvalue ссылка. Этот параметр абсолютно всеяден и может быть сопоставлен абсолютно любой категории выражений, не только rvalue, но и lvalue. Таким образом, эта функция абсолютно универсальна и отработает во всех случаях как надо. Небезопасность этой функции в том, что можно получать мертвые ссылки, если пользоваться ею бездумно. В то же время эта функция всегда будет безопасной при использовании в качестве подвыражения в составе других выражений.
Здравствуйте, Коваленко Дмитрий, Вы писали: КД>Здравствуйте, B0FEE664, Вы писали: КД>>>Досадно, что сценарий передачи временного объекта по неконстантной ссылке есть, а официального разрешения со стороны C++ нет BFE>>Как это нет? rvalue reference как раз для такого случая подходят. Просто добавь второй амперсанд и будет как надо: BFE>>
КД>В моей башке rvalue reference связано с перемещением состояния объекта. КД>А я мне тут ничего перемещать не надо
Есть такая замечательная штука, как RVO (Return Value Optimization). Которая позволяет не вызывать лишний раз конструктор копии или перемещения и передавать rvalue туда, где объект будет использоваться.
Я твой код немного модифицировал. Объявил add_error как void add_error(t_errors&& errs,int const errCode) и добавил вывод информации о некоторых функциях, об адресе и типе объектов, конструктор копии и деструктор, что бы было видно, когда они вызовутся.
Как видишь, адреса в конструкторе и в add_error() совпадают и конструктор копии не вызывается, т.е. — функция add_error() использует тот-же объект, который был возвращён из ctx.get_errors_without_limits() или ctx.get_errors_with_limits(). А при выходе из add_error() объект разрушается.
rvalue может использоваться не только для перемещения, но и для RVO.
А если объявить add_error как void add_error(t_errors& errs,int const errCode), то GCC ругается
rsdn.cpp: In function ‘int main()’:
rsdn.cpp:136:41: error: invalid initialization of non-const reference of type ‘t_errors&’ from an rvalue of type ‘t_errors’
add_error(ctx.get_errors_without_limits(),1);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
rsdn.cpp:121:6: note: initializing argument 1 of ‘void add_error(t_errors&, int)’
void add_error(t_errors& errs,int const errCode)
^~~~~~~~~
rsdn.cpp:138:38: error: invalid initialization of non-const reference of type ‘t_errors&’ from an rvalue of type ‘t_errors’
add_error(ctx.get_errors_with_limits(),2);
~~~~~~~~~~~~~~~~~~~~~~~~~~^~
rsdn.cpp:121:6: note: initializing argument 1 of ‘void add_error(t_errors&, int)’
void add_error(t_errors& errs,int const errCode)
^~~~~~~~~
Re[6]: Передача объекта, возвращаемого функцией, в другую фу
Здравствуйте, rg45, Вы писали:
КД>>Досадно, что сценарий передачи временного объекта по неконстантной ссылке есть, а официального разрешения со стороны C++ нет
R>Ну ОК, я могу предложить еще один способ забиндить временный объект к неконстантной ссылке. Я не хотел его показыать, поскольку он не совсем безопасен. Но, похоже, что для тебя этот способ хорошо подойдет, поскольку он вполне легален, и не требует добавления дополнительных перегрузок.
R>1. Определяем общую утилитную функцию make_lvalue (имя выбрать по вкусу):
Небольшой хак, форсирующий rvalue-reference до lvalue
template<class T>
T& var_cast(T&& var) { return var; }
...
A var;
const A cvar;
A val();
const A cval();
A& ref();
const A& cref();
void accept(A& r);
...
// всё ок
accept(var_cast(var));
accept(var_cast(val()));
accept(var_cast(ref()));
// не скомпилируется - результат имеет тип const A&
accept(var_cast(cvar));
accept(var_cast(cval()));
accept(var_cast(cref()));
Перекуём баги на фичи!
Re[3]: Передача объекта, возвращаемого функцией, в другую функц
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>В моей башке rvalue reference связано с перемещением состояния объекта. КД>А я мне тут ничего перемещать не надо
А в моей голове rvalue reference связана именно со временными объектами, хотя многие считают такую связь альтернативно одарённой, так что настаивать не буду, дабы не провоцировать свидетелей value category.
И каждый день — без права на ошибку...
Re[9]: Передача объекта, возвращаемого функцией, в другую фу
КД>Сначала у меня что-то в голове сдвинулось, но потом я понял, что в моем случае лучше сидеть на make_lvalue, предложенном rg45 КД>Решение с make_lvalue уже "ушло в HEAD" и прошло нагрузочное тестирование
Чо, когда у нас там получка?
--
Не можешь достичь желаемого — пожелай достигнутого.
Все откомпилировалось и работает без проблем. Предупреждений 4-го уровня тоже нет.
Где-то в глубине души меня беспокоит такая конструкция func1(&func2()). Но она компилируется и работает. То есть, временный объект, возвращаемый из func2, живет до конца вызова func1.
Как сделать лучше — не знаю.
Собственно вопрос — это нормальный подход или могут возникнуть проблем?
Еще раз отмечу, что add_current_errors указатель/ссылку на t_errors внутри не запоминает.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: Передача объекта, возвращаемого функцией, в другую функц
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Теперь есть утилита, работающая с t_errors: КД>
КД>void add_current_errors(t_errors& errs); //errs внутри не запоминается.
КД>
Тут должен быть const :
void add_current_errors(const t_errors& errs);
КД>Собственно вопрос — это нормальный подход или могут возникнуть проблем?
Подход не нормальный в том смысле, что не соответствует современным нормам.
Проблемы возникнуть могут, но они связаны не с работоспособностью кода (код рабочий), а с вопросами возникающими у читателя этого кода:
— если вы меняете errs внутри add_current_errors, то зачем вы это делаете?
— если вы не меняете errs внутри add_current_errors, то зачем он не const?
PS моя телепатия мне подсказывает, что у интерфейса t_errors есть виртуальные get-методы не обявленные const.
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Как сделать лучше — не знаю.
КД>Собственно вопрос — это нормальный подход или могут возникнуть проблем?
КД>Еще раз отмечу, что add_current_errors указатель/ссылку на t_errors внутри не запоминает.
Здравствуйте, andrey.desman, Вы писали:
КД>>Нет, const тут не нужен, потому что в errs добавляются новые объекты (описания ошибок).
AD>errs меняется, но потом тут же уничтожается. В чем смысл? Почему get_errors*() возвращает объект, а не ссылку на объект долгоживущий?
Да, errs меняется, потом уничтожается. Но в случае
Потому что объекты, возвращаемые get_errors_without_limits и get_errors_with_limits, внутри себя хранят только указатель на ctx, которому они все передают.
То есть get_errors_without_limits возвращает объект, который просто передает все, что в него засовывают, в свой родительский объект (ctx).
А get_errors_with_limits возвращает объект, который сначала проверяет ограничения, а потом передает добавляемые данные в свой родительский объект (ctx).
-----------
Я тут посмотрел на свою писанину... Сдается мне, начальное решение (с передачей по ссылке) было нормальным.
А то что оно не собирается в Conformance Mode=Yes, это проблемы этого Mode. Меня никто насильно его включать не заставляет
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[5]: Передача объекта, возвращаемого функцией, в другую фу
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>А то что оно не собирается в Conformance Mode=Yes, это проблемы этого Mode. Меня никто насильно его включать не заставляет
то что оно собирается в принципе -- это следствие того что когда-то майкрософт забил на стандарт.
другие компиляторы это не соберут.
если очень хочется, то или сделай add_current_errors шаблонным add_current_errors(TErr&&),
или в базу добавь метод, и вручную вызывай его:
, ошибку компиляции можно задавить с помощью *&. Но вылезут предупреждения 4-го уровня:
add_error(*&ctx.get_errors_without_limits(),1); //warning C4238: nonstandard extension used: class rvalue used as lvalue
add_error(*&ctx.get_errors_with_limits(),2); //warning C4238: nonstandard extension used: class rvalue used as lvalue
Можно переделать add_error, чтобы он получал указатель на errs. Тоже компилируется с предупреждениями 4-го уровня:
void add_error(t_errors* errs,int const errCode);
//...
add_error(&ctx.get_errors_without_limits(),1); //warning C4238: nonstandard extension used: class rvalue used as lvalue
add_error(&ctx.get_errors_with_limits(),2); //warning C4238: nonstandard extension used: class rvalue used as lvalue
//...
Как по мне, начальный вариант со ссылкой выглядел лучше.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
КД>Нет, const тут не нужен, потому что в errs добавляются новые объекты (описания ошибок).
КД>t_errors — это интерфейс формируемой коллекции ошибок.
Передавай тогда по значению, и меняй внутри как хочешь.
void add_current_errors(t_errors errs);
Re[2]: Передача объекта, возвращаемого функцией, в другую функц
Здравствуйте, Коваленко Дмитрий.
Если я правильно понял, то сами реализации "знают" о ctx, и их методы сами эти реализации не меняют, а меняют в самом ctx.
Почему бы не передать по константной ссылке эти реализации и сами методы не сделать константными?
Можно привести аналогию с указателями. Указатель на константу и константный указатель на не-константу — разные вещи. В вашем случае реализация — это условный константный указатель на не константный
объект ctx, поэтому ее методы по отношению к самой себе — константны.
Re[2]: Передача объекта, возвращаемого функцией, в другую функцию.
Здравствуйте, Went, Вы писали:
W>Здравствуйте, Коваленко Дмитрий. W>Если я правильно понял, то сами реализации "знают" о ctx, и их методы сами эти реализации не меняют, а меняют в самом ctx.
В данном случае, да. Вы все правильно поняли.
Но дело в том, что этот интерфейс t_errors еще реализован в другом классе, объекты которого сами хранят добавляемые объекты.
То есть метод "virtual void add(int errCode)=0;" константным сделать нельзя.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: Передача объекта, возвращаемого функцией, в другую функцию.
Здравствуйте, Коваленко Дмитрий, Вы писали: КД>Но дело в том, что этот интерфейс t_errors еще реализован в другом классе, объекты которого сами хранят добавляемые объекты. КД>То есть метод "virtual void add(int errCode)=0;" константным сделать нельзя.
Понятно... Ну, а написать
auto errors = ctx.get_errors_without_limits();
add_current_errors(errors);
нельзя?
Re[4]: Передача объекта, возвращаемого функцией, в другую функцию.
Здравствуйте, Went, Вы писали:
КД>>Но дело в том, что этот интерфейс t_errors еще реализован в другом классе, объекты которого сами хранят добавляемые объекты. КД>>То есть метод "virtual void add(int errCode)=0;" константным сделать нельзя. W>Понятно... Ну, а написать W>
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Досадно, что сценарий передачи временного объекта по неконстантной ссылке есть, а официального разрешения со стороны C++ нет
Как это нет? rvalue reference как раз для такого случая подходят. Просто добавь второй амперсанд и будет как надо:
void add_current_errors(t_errors&& errs);
И каждый день — без права на ошибку...
Re[6]: Передача объекта, возвращаемого функцией, в другую функцию.
Здравствуйте, B0FEE664, Вы писали:
КД>>Досадно, что сценарий передачи временного объекта по неконстантной ссылке есть, а официального разрешения со стороны C++ нет BFE>Как это нет? rvalue reference как раз для такого случая подходят. Просто добавь второй амперсанд и будет как надо: BFE>
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Привет всем.
КД>Есть интерфейс t_errors.
КД>У него две реализации — t_errors_without_limits и t_errors_with_limits
КД>Есть два метода, возвращающие экземпляры этих реализацией: КД>
КД>Все компилируется (VC++) и работает как и ожидается.
КД>При включенном 4-ом уровне предупреждений, компилятор предупреждает, что нельзя передавать по ссылке результат метода.
КД>Когда включаешь режим 'совместимости/соответствия' (Conformance Mode=Yes), компилятор отказывается компилировать.
КД>Переделал так: КД>
КД>Все откомпилировалось и работает без проблем. Предупреждений 4-го уровня тоже нет.
КД>Где-то в глубине души меня беспокоит такая конструкция func1(&func2()). Но она компилируется и работает. То есть, временный объект, возвращаемый из func2, живет до конца вызова func1.
КД>Как сделать лучше — не знаю.
КД>Собственно вопрос — это нормальный подход или могут возникнуть проблем?
КД>Еще раз отмечу, что add_current_errors указатель/ссылку на t_errors внутри не запоминает.
сдается мне, что предложенные солюшены небезопасны, либо при обновлении очередного компилятора МС приведут к потере компилябильности.
Такое поведение (продление жизни ссылки как для параметров так и для rvlue) сугубо майкрософт специфик, причем в последних версиях это может не работать.
Имхо просто врапперок над
add_current_errors что-то типа
template <typename Exc>
add_current_errors_fwd(Exc&& exc) {
add_current_errors(std::forward(exc));
}
поможет и код сохранить и особых нагрузок не добавит.
если это все идет в обощенную часть, можно и add_current_errors перегрузить для некоего враппера а из ctx.get_errors_w..._limits
мувать.
Compiler can be as trained AI but can't compose music.
Antheil piano jazz sonata. Я болен ПГМ.
Re[7]: Передача объекта, возвращаемого функцией, в другую фу
КД>Отлично, просто отлично. КД>Имя оставил как есть.
С именами здесь можно было бы и поприкалываться. Если следовать логике стандарной библиотеки, эта функция должна бы называться unmove, ибо она выполняет преобразование, обратное тому, которое выполняет std::move
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[8]: Передача объекта, возвращаемого функцией, в другую фу
Здравствуйте, rg45, Вы писали:
КД>>Имя оставил как есть.
R>С именами здесь можно было бы и поприкалываться. Если следовать логике стандарной библиотеки, эта функция должна бы называться unmove, ибо она выполняет преобразование, обратное тому, которое выполняет std::move
Спасибо за проведенный анализ проблемы.
AN>rvalue может использоваться не только для перемещения, но и для RVO.
Сначала у меня что-то в голове сдвинулось, но потом я понял, что в моем случае лучше сидеть на make_lvalue, предложенном rg45
У меня в add_error(errs,errCode) передаются как временные объекты (полученные из функций get_xxxx()), так и локальные. Поэтому параметр errs определен как ссылка.
add_error(errs,errCode) — может быть виртуальным методом. Поэтому его нельзя определить как шаблон.
---
Решение с make_lvalue уже "ушло в HEAD" и прошло нагрузочное тестирование
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: Передача объекта, возвращаемого функцией, в другую функц
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Привет всем.
КД>Есть интерфейс t_errors.
КД>У него две реализации — t_errors_without_limits и t_errors_with_limits
КД>Есть два метода, возвращающие экземпляры этих реализацией: КД>
в старых плюсах это чистое UB. Передача ссылки на временный объект на стеке, время жизни которого закончилось. В новых плюсах есть расширение времени жизни переменной. Но оно весьма ограничено. Ты уверен, что оно здесь сработало? см. мой вопрос — народ утверждает, что расширение времени жизни переменной через rvalue не работает. см. мой вопрос
Здравствуйте, AndrewJD, Вы писали:
NB>>то что оно собирается в принципе -- это следствие того что когда-то майкрософт забил на стандарт.
AJD>У них это было еще когда стандарта не существовало.
после выхода стандарта у них была куча времени это поправить.
Re[8]: Передача объекта, возвращаемого функцией, в другую фу