Re[2]: Передача объекта, возвращаемого функцией, в другую функцию.
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 01.10.19 11:13
Оценка:
Здравствуйте, Went, Вы писали:

W>Здравствуйте, Коваленко Дмитрий.

W>Если я правильно понял, то сами реализации "знают" о ctx, и их методы сами эти реализации не меняют, а меняют в самом ctx.

В данном случае, да. Вы все правильно поняли.

Но дело в том, что этот интерфейс t_errors еще реализован в другом классе, объекты которого сами хранят добавляемые объекты.

То есть метод "virtual void add(int errCode)=0;" константным сделать нельзя.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: Передача объекта, возвращаемого функцией, в другую функцию.
От: Went  
Дата: 01.10.19 11:36
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Но дело в том, что этот интерфейс t_errors еще реализован в другом классе, объекты которого сами хранят добавляемые объекты.
КД>То есть метод "virtual void add(int errCode)=0;" константным сделать нельзя.
Понятно... Ну, а написать
auto errors = ctx.get_errors_without_limits();
add_current_errors(errors);

нельзя?
Re[4]: Передача объекта, возвращаемого функцией, в другую функцию.
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 01.10.19 11:44
Оценка:
Здравствуйте, Went, Вы писали:

КД>>Но дело в том, что этот интерфейс t_errors еще реализован в другом классе, объекты которого сами хранят добавляемые объекты.

КД>>То есть метод "virtual void add(int errCode)=0;" константным сделать нельзя.
W>Понятно... Ну, а написать
W>
W>auto errors = ctx.get_errors_without_limits();
W>add_current_errors(errors);
W>

W>нельзя?

Можно, но как-то коряво.

Как я тут уже писал — пока компилятор терпит, оставлю как есть

---
Досадно, что сценарий передачи временного объекта по неконстантной ссылке есть, а официального разрешения со стороны C++ нет
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[5]: Передача объекта, возвращаемого функцией, в другую функцию.
От: B0FEE664  
Дата: 01.10.19 12:35
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Досадно, что сценарий передачи временного объекта по неконстантной ссылке есть, а официального разрешения со стороны C++ нет

Как это нет? rvalue reference как раз для такого случая подходят. Просто добавь второй амперсанд и будет как надо:
void add_current_errors(t_errors&& errs);
И каждый день — без права на ошибку...
Re[6]: Передача объекта, возвращаемого функцией, в другую функцию.
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 01.10.19 12:39
Оценка:
Здравствуйте, B0FEE664, Вы писали:

КД>>Досадно, что сценарий передачи временного объекта по неконстантной ссылке есть, а официального разрешения со стороны C++ нет

BFE>Как это нет? rvalue reference как раз для такого случая подходят. Просто добавь второй амперсанд и будет как надо:
BFE>
BFE>void add_current_errors(t_errors&& errs);
BFE>


В моей башке rvalue reference связано с перемещением состояния объекта.

А я мне тут ничего перемещать не надо
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[5]: Передача объекта, возвращаемого функцией, в другую фу
От: rg45 СССР  
Дата: 01.10.19 13:32
Оценка: 12 (1)
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>---

КД>Досадно, что сценарий передачи временного объекта по неконстантной ссылке есть, а официального разрешения со стороны C++ нет

Ну ОК, я могу предложить еще один способ забиндить временный объект к неконстантной ссылке. Я не хотел его показыать, поскольку он не совсем безопасен. Но, похоже, что для тебя этот способ хорошо подойдет, поскольку он вполне легален, и не требует добавления дополнительных перегрузок.

1. Определяем общую утилитную функцию make_lvalue (имя выбрать по вкусу):
template <typename T>
T& make_lvalue(T&& t)
{
   return t;
}


Замечу, на всякий случай, T&& здесь — это так называемая форвардная ссылка, хоть по внешнему виду и выглядит как rvalue ссылка. Этот параметр абсолютно всеяден и может быть сопоставлен абсолютно любой категории выражений, не только rvalue, но и lvalue. Таким образом, эта функция абсолютно универсальна и отработает во всех случаях как надо. Небезопасность этой функции в том, что можно получать мертвые ссылки, если пользоваться ею бездумно. В то же время эта функция всегда будет безопасной при использовании в качестве подвыражения в составе других выражений.

2. Ну и собсно использование:

add_current_errors(make_lvalue(ctx.get_errors_without_limits()));
//...
add_current_errors(make_lvalue(ctx.get_errors_with_limits()));
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 01.10.2019 13:37 rg45 . Предыдущая версия . Еще …
Отредактировано 01.10.2019 13:36 rg45 . Предыдущая версия .
Re[7]: Передача объекта, возвращаемого функцией, в другую функцию.
От: B0FEE664  
Дата: 01.10.19 13:44
Оценка: :)
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>В моей башке rvalue reference связано с перемещением состояния объекта.

КД>А я мне тут ничего перемещать не надо

А в моей голове rvalue reference связана именно со временными объектами, хотя многие считают такую связь альтернативно одарённой, так что настаивать не буду, дабы не провоцировать свидетелей value category.
И каждый день — без права на ошибку...
Re: Передача объекта, возвращаемого функцией, в другую функцию.
От: ollv СССР https://youtu.be/DQDoYs6wHoo
Дата: 01.10.19 15:29
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Привет всем.


КД>Есть интерфейс t_errors.


КД>У него две реализации — t_errors_without_limits и t_errors_with_limits


КД>Есть два метода, возвращающие экземпляры этих реализацией:

КД>
КД>t_errors_without_limits get_errors_without_limits();

КД>t_errors_with_limits get_errors_with_limits();
КД>

КД>Теперь есть утилита, работающая с t_errors:
КД>
КД>void add_current_errors(t_errors& errs); //errs внутри не запоминается.
КД>

КД>Она вызывается приблизительно так:
КД>
КД>add_current_errors(ctx.get_errors_without_limits());
КД>//...
КД>add_current_errors(ctx.get_errors_with_limits());
КД>

КД>Все компилируется (VC++) и работает как и ожидается.

КД>При включенном 4-ом уровне предупреждений, компилятор предупреждает, что нельзя передавать по ссылке результат метода.


КД>Когда включаешь режим 'совместимости/соответствия' (Conformance Mode=Yes), компилятор отказывается компилировать.


КД>Переделал так:

КД>
КД>void add_current_errors(t_errors*);
КД>//...
КД>add_current_errors(&ctx.get_errors_without_limits());
КД>//...
КД>add_current_errors(&ctx.get_errors_with_limits());
КД>

КД>Все откомпилировалось и работает без проблем. Предупреждений 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[6]: Передача объекта, возвращаемого функцией, в другую фу
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 02.10.19 13:22
Оценка: 9 (1)
Здравствуйте, rg45, Вы писали:

КД>>Досадно, что сценарий передачи временного объекта по неконстантной ссылке есть, а официального разрешения со стороны C++ нет


R>Ну ОК, я могу предложить еще один способ забиндить временный объект к неконстантной ссылке. Я не хотел его показыать, поскольку он не совсем безопасен. Но, похоже, что для тебя этот способ хорошо подойдет, поскольку он вполне легален, и не требует добавления дополнительных перегрузок.


R>1. Определяем общую утилитную функцию make_lvalue (имя выбрать по вкусу):

template <typename T>
T& make_lvalue(T&& t)
{
   return t;
}

Отлично, просто отлично.

Имя оставил как есть.

Спасибо!
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[7]: Передача объекта, возвращаемого функцией, в другую фу
От: rg45 СССР  
Дата: 03.10.19 10:54
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

R>>1. Определяем общую утилитную функцию make_lvalue (имя выбрать по вкусу):

КД>
КД>template <typename T>
КД>T& make_lvalue(T&& t)
КД>{
КД>   return t;
КД>}
КД>


КД>Отлично, просто отлично.

КД>Имя оставил как есть.

С именами здесь можно было бы и поприкалываться. Если следовать логике стандарной библиотеки, эта функция должна бы называться unmove, ибо она выполняет преобразование, обратное тому, которое выполняет std::move
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[8]: Передача объекта, возвращаемого функцией, в другую фу
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 03.10.19 11:52
Оценка:
Здравствуйте, rg45, Вы писали:

КД>>Имя оставил как есть.


R>С именами здесь можно было бы и поприкалываться. Если следовать логике стандарной библиотеки, эта функция должна бы называться unmove, ибо она выполняет преобразование, обратное тому, которое выполняет std::move


 //пример использования
 return this->RowRefresh__All
         (RowRefreshAnalyzer,
          pProcessedRows,
          structure::make_lvalue(CallCtx.ErrorsWithLimit()));


Из наиболее подходящего, что мне пришло в голову — make_var

-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: Передача объекта, возвращаемого функцией, в другую функцию.
От: Кодт Россия  
Дата: 08.10.19 11:53
Оценка: 5 (1)
Здравствуйте, Коваленко Дмитрий, Вы писали:

Небольшой хак, форсирующий 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[7]: Передача объекта, возвращаемого функцией, в другую функцию.
От: AleksandrN Россия  
Дата: 08.10.19 22:05
Оценка: 12 (1)
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Здравствуйте, B0FEE664, Вы писали:


КД>>>Досадно, что сценарий передачи временного объекта по неконстантной ссылке есть, а официального разрешения со стороны C++ нет

BFE>>Как это нет? rvalue reference как раз для такого случая подходят. Просто добавь второй амперсанд и будет как надо:
BFE>>
BFE>>void add_current_errors(t_errors&& errs);
BFE>>


КД>В моей башке rvalue reference связано с перемещением состояния объекта.


КД>А я мне тут ничего перемещать не надо


Есть такая замечательная штука, как RVO (Return Value Optimization). Которая позволяет не вызывать лишний раз конструктор копии или перемещения и передавать rvalue туда, где объект будет использоваться.
Я твой код немного модифицировал. Объявил add_error как void add_error(t_errors&& errs,int const errCode) и добавил вывод информации о некоторых функциях, об адресе и типе объектов, конструктор копии и деструктор, что бы было видно, когда они вызовутся.
  код
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <typeinfo>

////////////////////////////////////////////////////////////////////////////////
//class t_errors

class t_errors
{
 public:
  virtual void add(int errCode)=0;
    void print()
    {
        std::cout << (void*)this << " : " << typeid(*this).name() << "\n";
    }
};

////////////////////////////////////////////////////////////////////////////////
//class t_context

class t_context
{
 private:
  t_context(const t_context&)=delete;
  t_context& operator = (const t_context&)=delete;

 public:
  class tag_errs_without_limits:public t_errors
  {
   public:
    explicit tag_errs_without_limits(t_context* const pCtx):m_pCtx(pCtx)
    {
        std::cout << __PRETTY_FUNCTION__ << "\n";
        print();
    }

    tag_errs_without_limits(const tag_errs_without_limits &_):m_pCtx(_.m_pCtx)
    {
        std::cout << __PRETTY_FUNCTION__ << "\n";
        print();
    }

    ~tag_errs_without_limits()
    {
        std::cout << __PRETTY_FUNCTION__ << "\n";
        print();
    }

    virtual void add(int errCode)override
    {
          m_pCtx->helper__add_error(errCode);
          std::cout << __PRETTY_FUNCTION__ << "\n";
          print();
    }

   private:
    t_context* m_pCtx;
  };//class tag_errs_without_limits

 public:
  class tag_errs_with_limits:public t_errors
  {
   public:
    explicit tag_errs_with_limits(t_context* const pCtx):m_pCtx(pCtx)
    {
        std::cout << __PRETTY_FUNCTION__ << "\n";
        print();
    }

    tag_errs_with_limits(const tag_errs_with_limits &_):m_pCtx(_.m_pCtx)
    {
        std::cout << __PRETTY_FUNCTION__ << "\n";
        print();
    }

    ~tag_errs_with_limits()
    {
        std::cout << __PRETTY_FUNCTION__ << "\n";
        print();
    }

    virtual void add(int errCode)override
    {
          /*check limits*/
          m_pCtx->helper__add_error(errCode);
          std::cout << __PRETTY_FUNCTION__ << "\n";
          print();
    }

   private:
    t_context* m_pCtx;
  };//class tag_errs_with_limits

 public:
  t_context()
  {}

  tag_errs_without_limits get_errors_without_limits()
  {
    std::cout << __PRETTY_FUNCTION__ << "\n";
    return tag_errs_without_limits(this);
  }//get_errors_without_limits

  tag_errs_with_limits get_errors_with_limits()
  {
    std::cout << __PRETTY_FUNCTION__ << "\n";
    return tag_errs_with_limits(this);
  }//get_errors_with_limits

 private:
  void helper__add_error(int const errCode)
  {
   std::cout<<"helper__add_error: "<<errCode<<"!\n";
  }
};

////////////////////////////////////////////////////////////////////////////////

//add_error - может быть виртуальным методом некоторого класса

void add_error(t_errors&& errs,int const errCode)
{
    std::cout << __PRETTY_FUNCTION__ << "\n";
    errs.print();

    errs.add(errCode);

}//add_error

////////////////////////////////////////////////////////////////////////////////

int main()
{
 t_context ctx;

 add_error(ctx.get_errors_without_limits(),1);
 std::cout << "\n";
 add_error(ctx.get_errors_with_limits(),2);

 return 0;
}//main
////////////////////////////////////////////////////////////////////////////////


Собирал GCC. Пример вывода:
t_context::tag_errs_without_limits t_context::get_errors_without_limits()
t_context::tag_errs_without_limits::tag_errs_without_limits(t_context*)
0x7ffcddc03640 : N9t_context23tag_errs_without_limitsE
void add_error(t_errors&&, int)
0x7ffcddc03640 : N9t_context23tag_errs_without_limitsE
helper__add_error: 1!
virtual void t_context::tag_errs_without_limits::add(int)
0x7ffcddc03640 : N9t_context23tag_errs_without_limitsE
t_context::tag_errs_without_limits::~tag_errs_without_limits()
0x7ffcddc03640 : N9t_context23tag_errs_without_limitsE

t_context::tag_errs_with_limits t_context::get_errors_with_limits()
t_context::tag_errs_with_limits::tag_errs_with_limits(t_context*)
0x7ffcddc03650 : N9t_context20tag_errs_with_limitsE
void add_error(t_errors&&, int)
0x7ffcddc03650 : N9t_context20tag_errs_with_limitsE
helper__add_error: 2!
virtual void t_context::tag_errs_with_limits::add(int)
0x7ffcddc03650 : N9t_context20tag_errs_with_limitsE
t_context::tag_errs_with_limits::~tag_errs_with_limits()
0x7ffcddc03650 : N9t_context20tag_errs_with_limitsE


Как видишь, адреса в конструкторе и в 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[8]: Передача объекта, возвращаемого функцией, в другую функцию.
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 09.10.19 08:47
Оценка:
Здравствуйте, AleksandrN, Вы писали:

Спасибо за проведенный анализ проблемы.

AN>rvalue может использоваться не только для перемещения, но и для RVO.


Сначала у меня что-то в голове сдвинулось, но потом я понял, что в моем случае лучше сидеть на make_lvalue, предложенном rg45

У меня в add_error(errs,errCode) передаются как временные объекты (полученные из функций get_xxxx()), так и локальные. Поэтому параметр errs определен как ссылка.

add_error(errs,errCode) — может быть виртуальным методом. Поэтому его нельзя определить как шаблон.

---
Решение с make_lvalue уже "ушло в HEAD" и прошло нагрузочное тестирование
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[9]: Передача объекта, возвращаемого функцией, в другую фу
От: rg45 СССР  
Дата: 10.10.19 08:35
Оценка: :)
Здравствуйте, Коваленко Дмитрий, Вы писали:


КД>Сначала у меня что-то в голове сдвинулось, но потом я понял, что в моем случае лучше сидеть на make_lvalue, предложенном rg45

КД>Решение с make_lvalue уже "ушло в HEAD" и прошло нагрузочное тестирование

Чо, когда у нас там получка?
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 10.10.2019 8:37 rg45 . Предыдущая версия .
Re: Передача объекта, возвращаемого функцией, в другую функц
От: Molchalnik  
Дата: 08.11.19 03:51
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Привет всем.


КД>Есть интерфейс t_errors.


КД>У него две реализации — t_errors_without_limits и t_errors_with_limits


КД>Есть два метода, возвращающие экземпляры этих реализацией:

КД>
КД>t_errors_without_limits get_errors_without_limits();
КД>

КД>
КД>add_current_errors(ctx.get_errors_without_limits());
КД>//...
КД>add_current_errors(ctx.get_errors_with_limits());
КД>


в старых плюсах это чистое UB. Передача ссылки на временный объект на стеке, время жизни которого закончилось. В новых плюсах есть расширение времени жизни переменной. Но оно весьма ограничено. Ты уверен, что оно здесь сработало? см. мой вопрос — народ утверждает, что расширение времени жизни переменной через rvalue не работает. см. мой вопрос
Автор: Molchalnik
Дата: 08.11.19


если так, то беря адрес и разыменовывая его, ты дуришь компилер, но не убираешь UB
Отредактировано 08.11.2019 4:03 Molchalnik . Предыдущая версия .
Re[6]: Передача объекта, возвращаемого функцией, в другую фу
От: AndrewJD США  
Дата: 08.11.19 17:51
Оценка:
Здравствуйте, night beast, Вы писали:

NB>то что оно собирается в принципе -- это следствие того что когда-то майкрософт забил на стандарт.


У них это было еще когда стандарта не существовало.
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re[7]: Передача объекта, возвращаемого функцией, в другую фу
От: night beast СССР  
Дата: 08.11.19 18:00
Оценка:
Здравствуйте, AndrewJD, Вы писали:

NB>>то что оно собирается в принципе -- это следствие того что когда-то майкрософт забил на стандарт.


AJD>У них это было еще когда стандарта не существовало.


после выхода стандарта у них была куча времени это поправить.
Re[8]: Передача объекта, возвращаемого функцией, в другую фу
От: AndrewJD США  
Дата: 08.11.19 18:26
Оценка:
Здравствуйте, night beast, Вы писали:

NB>после выхода стандарта у них была куча времени это поправить.


Согласен
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.