Передача объекта, возвращаемого функцией, в другую функцию.
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 26.09.19 16:17
Оценка:
Привет всем.

Есть интерфейс 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 внутри не запоминает.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: Передача объекта, возвращаемого функцией, в другую функцию.
От: night beast СССР  
Дата: 26.09.19 16:26
Оценка: +3
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>У него две реализации — 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-ом уровне предупреждений, компилятор предупреждает, что нельзя передавать по ссылке результат метода.


errs внутри add_current_errors изменяется? если нет, поставь const.
какой другой компилятор тебя и за взятие адреса rvalue отругает, так что не решение.
Re: Передача объекта, возвращаемого функцией, в другую функц
От: B0FEE664  
Дата: 26.09.19 16:30
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Теперь есть утилита, работающая с 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.
И каждый день — без права на ошибку...
Отредактировано 26.09.2019 16:37 B0FEE664 . Предыдущая версия .
Re: Передача объекта, возвращаемого функцией, в другую функц
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 26.09.19 16:54
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

Надо было немного более подробно описать задачу:

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


С виртуальным методом void add(t_error* pError)=0;

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


Соответственно, эти два класса реализуют метод add.

КД>Теперь есть утилита, работающая с t_errors:

void add_current_errors(t_errors& errs); //errs внутри не запоминается.

Эта утилита вызывает errs.add(...)

UPD1. Название add_current_errors не очень удачное. Это утилита, которая добавляет новые объекты в errs.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Отредактировано 26.09.2019 17:02 DDDX . Предыдущая версия .
Re[2]: Передача объекта, возвращаемого функцией, в другую функц
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 26.09.19 16:57
Оценка:
Здравствуйте, B0FEE664, Вы писали:

КД>>Теперь есть утилита, работающая с t_errors:

void add_current_errors(t_errors& errs); //errs внутри не запоминается.

BFE>Тут должен быть const :
void add_current_errors(const t_errors& errs);

Нет, const тут не нужен, потому что в errs добавляются новые объекты (описания ошибок).

t_errors — это интерфейс формируемой коллекции ошибок.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: Передача объекта, возвращаемого функцией, в другую функцию.
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 26.09.19 17:06
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Как сделать лучше — не знаю.


КД>Собственно вопрос — это нормальный подход или могут возникнуть проблем?


КД>Еще раз отмечу, что add_current_errors указатель/ссылку на t_errors внутри не запоминает.


Кстати, проблему можно было задавить так:

add_current_errors(*&ctx.get_errors_without_limits());
//...
add_current_errors(*&ctx.get_errors_with_limits());


Но я решил что это уже совсем ... плохо
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: Передача объекта, возвращаемого функцией, в другую функц
От: andrey.desman  
Дата: 26.09.19 17:19
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Нет, const тут не нужен, потому что в errs добавляются новые объекты (описания ошибок).


errs меняется, но потом тут же уничтожается. В чем смысл? Почему get_errors*() возвращает объект, а не ссылку на объект долгоживущий?
Re[4]: Передача объекта, возвращаемого функцией, в другую функц
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 26.09.19 18:03
Оценка:
Здравствуйте, andrey.desman, Вы писали:

КД>>Нет, const тут не нужен, потому что в errs добавляются новые объекты (описания ошибок).


AD>errs меняется, но потом тут же уничтожается. В чем смысл? Почему get_errors*() возвращает объект, а не ссылку на объект долгоживущий?


Да, errs меняется, потом уничтожается. Но в случае

add_current_errors(/*errs*/ctx.get_errors_without_limits());


все будет сохранено в ctx.

Потому что объекты, возвращаемые 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]: Передача объекта, возвращаемого функцией, в другую фу
От: night beast СССР  
Дата: 26.09.19 18:21
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>А то что оно не собирается в Conformance Mode=Yes, это проблемы этого Mode. Меня никто насильно его включать не заставляет


то что оно собирается в принципе -- это следствие того что когда-то майкрософт забил на стандарт.
другие компиляторы это не соберут.
если очень хочется, то или сделай add_current_errors шаблонным add_current_errors(TErr&&),
или в базу добавь метод, и вручную вызывай его:
struct t_errors
{
    t_errors& get_ref() { return *this; }
};
Отредактировано 26.09.2019 18:22 night beast . Предыдущая версия .
Re: Передача объекта, возвращаемого функцией, в другую функц
От: rg45 СССР  
Дата: 26.09.19 18:46
Оценка: +3
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Есть интерфейс 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); }
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 26.09.2019 18:51 rg45 . Предыдущая версия . Еще …
Отредактировано 26.09.2019 18:50 rg45 . Предыдущая версия .
Re: Передача объекта, возвращаемого функцией, в другую функц
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 27.09.19 04:36
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

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


Тут одна мысль посетила, благодаря rg45
Автор: rg45
Дата: 26.09.19
. Но я решил с ней повременить.
  И привести нормальный пример описываемой проблемы (VS2019)
////////////////////////////////////////////////////////////////////////////////
#include <iostream>

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

class t_errors
{
 public:
  virtual void add(int errCode)=0;
};

////////////////////////////////////////////////////////////////////////////////
//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){;}

    virtual void add(int errCode)override {m_pCtx->helper__add_error(errCode);}

   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){;}

    virtual void add(int errCode)override {/*check limits*/; m_pCtx->helper__add_error(errCode);}

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

 public:
  t_context()
  {}

  tag_errs_without_limits get_errors_without_limits()
  {
   return tag_errs_without_limits(this);
  }//get_errors_without_limits

  tag_errs_with_limits get_errors_with_limits()
  {
   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)
{
 errs.add(errCode);
}//add_error

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

int main()
{
 t_context ctx;

 add_error(ctx.get_errors_without_limits(),1);
 add_error(ctx.get_errors_with_limits(),2);

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

  Как это сейчас модно говорить, Real World Code (RWC) выглядит так:
//...
  virtual void GetData_AddErrorRecord__CantGetData
                               (typename call_ctx_type::errors_type& Errors,
                                const TBaseColumnsInfoImpl*          pColumnsInfo,
                                const DBBINDING&                     Binding,
                                const void*                          pData,
                                HRESULT                              get_data_hr)=0;
//...
template<class TStorageTraits>
HRESULT TMemRowsetStorageROImpl<TStorageTraits>::GetData
                                    (call_ctx_type&                    CallCtx,
                                     TRowBmk                     const Bmk,
                                     const TBindingArray*        const pBindingArray,
                                     const TBaseColumnsInfoImpl* const pColumnsInfo,
                                     void*                       const pData)
{
 //....
     if(get_data_hr!=S_OK)
     {
      this->GetData_AddErrorRecord__CantGetData
       (CallCtx.ErrorsWithLimit(), //<---------------- Вот проблемная точка
        pColumnsInfo,
        *pBinding,
        pData,
        get_data_hr); //no throw

      ++cntErrors;
     }//if
 //....
}//GetData
//...

Когда Conformance Mode=Yes, пример не компилируется:
Error    C2664     'void add_error(t_errors &,const int)': cannot convert argument 1 from 't_context::tag_errs_without_limits' to 't_errors &'
Error    C2664     'void add_error(t_errors &,const int)': cannot convert argument 1 from 't_context::tag_errs_with_limits' to 't_errors &'

Как я тут уже отмечал
Автор: Коваленко Дмитрий
Дата: 26.09.19
, ошибку компиляции можно задавить с помощью *&. Но вылезут предупреждения 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
//...


Как по мне, начальный вариант со ссылкой выглядел лучше.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Отредактировано 27.09.2019 5:01 DDDX . Предыдущая версия .
Re[2]: Передача объекта, возвращаемого функцией, в другую функц
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 27.09.19 05:14
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

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


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


КД>Тут одна мысль посетила, благодаря rg45
Автор: rg45
Дата: 26.09.19
. Но я решил с ней повременить.

КД>И привести нормальный пример описываемой проблемы (VS2019)

  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){;}

    virtual void add(int errCode)override {m_pCtx->helper__add_error(errCode);}

   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){;}

    virtual void add(int errCode)override {/*check limits*/; m_pCtx->helper__add_error(errCode);}

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

 public:
  t_context()
   :m_errs_without_limits(this)
   ,m_errs_with_limits(this)
  {}

  tag_errs_without_limits& get_errors_without_limits()
  {
   return m_errs_without_limits;
  }//get_errors_without_limits

  tag_errs_with_limits& get_errors_with_limits()
  {
   return m_errs_with_limits;
  }//get_errors_with_limits

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

 private:
  tag_errs_without_limits m_errs_without_limits;
  tag_errs_with_limits m_errs_with_limits;
};//class t_context

Тогда пример собирается с Conformance Mode=yes и без предупреждений 4-го уровня.

Как запасной вариант, если компилятор однажды загонит в угол
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: Передача объекта, возвращаемого функцией, в другую функц
От: Igore Россия  
Дата: 27.09.19 06:52
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

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


КД>>>Теперь есть утилита, работающая с t_errors:

КД>
КД>void add_current_errors(t_errors& errs); //errs внутри не запоминается.
КД>

BFE>>Тут должен быть const :
КД>
КД>void add_current_errors(const t_errors& errs);
КД>

КД>Нет, const тут не нужен, потому что в errs добавляются новые объекты (описания ошибок).

КД>t_errors — это интерфейс формируемой коллекции ошибок.

Передавай тогда по значению, и меняй внутри как хочешь.
void add_current_errors(t_errors errs);
Re[4]: Передача объекта, возвращаемого функцией, в другую функц
От: rg45 СССР  
Дата: 27.09.19 07:23
Оценка: +3
Здравствуйте, Igore, Вы писали:

КД>>
КД>>void add_current_errors(const t_errors& errs);
КД>>

КД>>Нет, const тут не нужен, потому что в errs добавляются новые объекты (описания ошибок).
КД>>t_errors — это интерфейс формируемой коллекции ошибок.

I>Передавай тогда по значению, и меняй внутри как хочешь.

I>void add_current_errors(t_errors errs);

Ну и будет срезка типа. А если t_errors — чисто абстрактный класс, то и вовсе ошибка компиляции.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Передача объекта, возвращаемого функцией, в другую функц
От: B0FEE664  
Дата: 27.09.19 07:53
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

Я переделал бы так:
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){;}

    virtual void add(int errCode)override {m_pCtx->helper__add_error(errCode);}

   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){;}

    virtual void add(int errCode)override {/*check limits*/; m_pCtx->helper__add_error(errCode);}

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

private:
  tag_errs_without_limits m_TagErrsWithoutLimits;
  tag_errs_with_limits    m_TagErrsWithLimits;
  public:
  t_context()
    : m_TagErrsWithoutLimits(this),
      m_TagErrsWithLimits(this)
  {}

  tag_errs_without_limits& get_errors_without_limits()
  {
   return m_TagErrsWithoutLimits;
  }//get_errors_without_limits

  tag_errs_with_limits& get_errors_with_limits()
  {
   return m_TagErrsWithLimits;
  }//get_errors_with_limits

 private:
  void helper__add_error(int const errCode)
  {
   std::cout<<"helper__add_error: "<<errCode<<"!\n";
  }
};
И каждый день — без права на ошибку...
Re[3]: Передача объекта, возвращаемого функцией, в другую функц
От: rg45 СССР  
Дата: 27.09.19 08:04
Оценка: +1
Здравствуйте, B0FEE664, Вы писали:

BFE>Я переделал бы так:

BFE>
BFE>  t_context()
BFE>    : m_TagErrsWithoutLimits(this),
BFE>      m_TagErrsWithLimits(this)
BFE>  {}
BFE>


Использование this в списке инициализации может создавать варнинги.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Передача объекта, возвращаемого функцией, в другую функц
От: Mr.Delphist  
Дата: 27.09.19 16:05
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:


КД>Тут одна мысль посетила, благодаря rg45
Автор: rg45
Дата: 26.09.19
. Но я решил с ней повременить.

КД>[cut=И привести нормальный пример описываемой проблемы (VS2019)]

Не знаю, у меня на VS2017 этот пример сразу выдаёт на add_error-вызовах:

error C2664: 'void add_error(t_errors &,const int)': cannot convert argument 1 from 't_context::tag_errs_without_limits' to 't_errors &'
error C2664: 'void add_error(t_errors &,const int)': cannot convert argument 1 from 't_context::tag_errs_with_limits' to 't_errors &'


Настройки дефолтные, даже warning level не менял. Точно этот код?
Re[3]: Передача объекта, возвращаемого функцией, в другую функц
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 27.09.19 17:52
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

MD>Не знаю, у меня на VS2017 этот пример сразу выдаёт на add_error-вызовах:

MD>

MD>error C2664: 'void add_error(t_errors &,const int)': cannot convert argument 1 from 't_context::tag_errs_without_limits' to 't_errors &'
MD>error C2664: 'void add_error(t_errors &,const int)': cannot convert argument 1 from 't_context::tag_errs_with_limits' to 't_errors &'


MD>Настройки дефолтные, даже warning level не менял. Точно этот код?


  Поставь здесь NO
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: Передача объекта, возвращаемого функцией, в другую функцию.
От: TailWind  
Дата: 29.09.19 13:15
Оценка:
t_errors E;  GetErrors(E); // получаем ошибки

DoSomething(E);  // обрабатываем
Re: Передача объекта, возвращаемого функцией, в другую функцию.
От: Went  
Дата: 01.10.19 11:04
Оценка:
Здравствуйте, Коваленко Дмитрий.
Если я правильно понял, то сами реализации "знают" о ctx, и их методы сами эти реализации не меняют, а меняют в самом ctx.
Почему бы не передать по константной ссылке эти реализации и сами методы не сделать константными?
Можно привести аналогию с указателями. Указатель на константу и константный указатель на не-константу — разные вещи. В вашем случае реализация — это условный константный указатель на не константный
объект ctx, поэтому ее методы по отношению к самой себе — константны.
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...
Пока на собственное сообщение не было ответов, его можно удалить.