Как вернуть NonCopyable объект?
От: Basil2 Россия https://starostin.msk.ru
Дата: 24.12.15 10:54
Оценка:
Есть код:

class MyClass : NonCopyable {}

MyClass foo()
{
    return MyClass();
}

Он прекрасно компилируется в MSVC 2010 и в Clang — видимо, срабатывает оптимизация возвращаемого значения и копирующий конструктор не требуется.

Однако код не компилируется в MSVC 2015 — пишет "referencing deleted function", указывая на копирующий конструктор. Конструкция
return {};
спасает ситуацию, но она в свою очередь не компилируется в MSVC 2010.

Вопрос — как красиво выйти из ситуации, чтобы код собирался одновременно под 2010, 2015 и clang?
Условную компиляцию и отказ от noncopyable не предлагать


UPD:
Проблема решена — добавлен конструктор перемещения.
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Отредактировано 24.12.2015 14:20 Basil2 . Предыдущая версия . Еще …
Отредактировано 24.12.2015 11:47 Basil2 (описка) . Предыдущая версия .
Re: Как вернуть NonCopyable объект?
От: uzhas Ниоткуда  
Дата: 24.12.15 11:15
Оценка: 4 (1)
Здравствуйте, Basil2, Вы писали:

B>Вопрос — как красиво выйти из ситуации, чтобы код собирался одновременно под 2010, 2015 и clang?


по стандарту нужна возможность перемещения (даже если компилятор ее по факту не использует). move конструктор объявить не хотите? можно поддержать noncopyable_but_movable
ссылки по теме:
http://stackoverflow.com/questions/19825152/diffrence-between-ctor-and-when-returning-non-movable-non-copyable-object
http://www.boost.org/doc/libs/master/doc/html/move/move_return.html
Re: Как вернуть NonCopyable объект?
От: _hum_ Беларусь  
Дата: 24.12.15 11:35
Оценка: +1
Здравствуйте, Basil2, Вы писали:

B>Есть код:


B>
B>class MyClass : NonCopyable {}

B>MyClass foo()
B>{
B>    return MyClass();
B>}
B>

B>Он прекрасно компилируется в MSVC 2010 и в Clang — видимо, срабатывает оптимизация возвращаемого значения и копирующий конструктор не требуется.

ничего себе. и, что, даже предупреждения не выдает, что "на самом деле у вас ошибка, но после нашей оптимизации и выбрасывания кода, все можно считать ок"?

B>Однако код не компилируется в MSVC 2015 — пишет "referencing deleted function", указывая на копирующий конструктор.


фух, значит, все-таки это был баг vs2010 (возможность компиляции такой явно некорректной проги)


Конструкция
B>
B>return MyClass{}
B>
спасает ситуацию, но она в свою очередь не компилируется в MSVC 2010.


кхм... а точно return MyClass{}, а не return {} ? Иначе я опять начинаю сомневаться в компиляторе (ведь MyClass{} же равносильно MyClass())

B>Вопрос — как красиво выйти из ситуации, чтобы код собирался одновременно под 2010, 2015 и clang?

B>Условную компиляцию и отказ от noncopyable не предлагать

странный вопрос. вы делает объект некопируемым и при этом хотите его скопировать. может, все-таки пересмотреть, что вам нужно на самом деле.
Re[2]: Как вернуть NonCopyable объект?
От: Basil2 Россия https://starostin.msk.ru
Дата: 24.12.15 11:48
Оценка:
Здравствуйте, _hum_, Вы писали:

__>ничего себе. и, что, даже предупреждения не выдает, что "на самом деле у вас ошибка, но после нашей оптимизации и выбрасывания кода, все можно считать ок"?


Да, без предупреждений.

__>кхм... а точно return MyClass{}, а не return {} ? Иначе я опять начинаю сомневаться в компиляторе (ведь MyClass{} же равносильно MyClass())


Да, просто {} — описался.

B>>Вопрос — как красиво выйти из ситуации, чтобы код собирался одновременно под 2010, 2015 и clang?

B>>Условную компиляцию и отказ от noncopyable не предлагать
__>странный вопрос. вы делает объект некопируемым и при этом хотите его скопировать. может, все-таки пересмотреть, что вам нужно на самом деле.

Здесь нет потребности в копировании.
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Re[2]: Как вернуть NonCopyable объект?
От: Basil2 Россия https://starostin.msk.ru
Дата: 24.12.15 11:55
Оценка:
Здравствуйте, uzhas, Вы писали:

U>move конструктор объявить не хотите?


Спасибо, про самый простой способ я и не подумал Точнее подумал, но думал что этого в 2010 еще нет...
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Re[3]: Как вернуть NonCopyable объект?
От: _hum_ Беларусь  
Дата: 24.12.15 12:00
Оценка: +1
Здравствуйте, Basil2, Вы писали:

B>Здравствуйте, _hum_, Вы писали:


B>>>Вопрос — как красиво выйти из ситуации, чтобы код собирался одновременно под 2010, 2015 и clang?

B>>>Условную компиляцию и отказ от noncopyable не предлагать
__>>странный вопрос. вы делает объект некопируемым и при этом хотите его скопировать. может, все-таки пересмотреть, что вам нужно на самом деле.

B>Здесь нет потребности в копировании.


а в чем есть потребность? зачем вы пытаетесь вернуть объект по значению, а не по ссылке, например?
Re[3]: Как вернуть NonCopyable объект?
От: _hum_ Беларусь  
Дата: 24.12.15 12:06
Оценка:
Здравствуйте, Basil2, Вы писали:

B>Здравствуйте, uzhas, Вы писали:


U>>move конструктор объявить не хотите?


B>Спасибо, про самый простой способ я и не подумал Точнее подумал, но думал что этого в 2010 еще нет...


"самый простой" — ой-ли?
Re[2]: Как вернуть NonCopyable объект?
От: T4r4sB Россия  
Дата: 24.12.15 19:38
Оценка:
Здравствуйте, _hum_, Вы писали:

__>фух, значит, все-таки это был баг vs2010 (возможность компиляции такой явно некорректной проги)


А может, обычное поведение ++03, сразу создавать объект на том месте, где он будет нужен?
Re[2]: Как вернуть NonCopyable объект?
От: Evgeny.Panasyuk Россия  
Дата: 24.12.15 20:00
Оценка:
Здравствуйте, uzhas, Вы писали:

U>по стандарту нужна возможность перемещения (даже если компилятор ее по факту не использует).


При использовании copy-list-initialization — return {init}; не нужна.
Re: Как вернуть NonCopyable объект?
От: Molchalnik  
Дата: 25.12.15 07:35
Оценка: -3
Здравствуйте, Basil2, Вы писали:


B>Вопрос — как красиво выйти из ситуации, чтобы код собирался одновременно под 2010, 2015 и clang?

B>Условную компиляцию и отказ от noncopyable не предлагать


B>UPD:

B>Проблема решена — добавлен конструктор перемещения.

Не уверен, что это красиво. Ибо совершается избыточная работа по перемещению. На мой взгляд, красиво — это либо создать его динамически и вернуть указатель/ссылку (например, через фабрику). Либо конструирование объекта по ссылке, вроде

void ConstructMyObject(MyObject & construct_this) {
  //инициализация объекта
  //...
}

int main(int argc, char * argv[]) {
  MyObject object();
  ConstructMyObject(object);
}


Третий подход, который видится мне, состоит в том, чтобы создать класс — хэндл, ссылочный класс, который бы автоматически приводился бы к данному. Тогда класс динамически можно было бы создать в любом месте, а копировать на него ссылочный класс. Специфично, но иногда интересно.

Четвёртый подход — это синглтон с меткой

template <typename Data, typename Label> Singleton { // синглтон, шаблонный параметр Label нигде не используется, но позволяет создавать несколько синглтонов типа Data
  //реализация синглтона, доступ через operator()
};

class MyObjectData : public NonCopyable {
  //определение своего класса
};

struct MyObject_ForUsingInSortAlgorithm_Label {}; // пустой класс, который играет роль метки

typedef Singleton<MyObjectData, MyObject_ForUsingInSortAlgorithm_Label> MyObject;

void MyObjectInitialize (MyObject data){
 data().set_some_arg(10);
 data().set_some_string_arg("Follow the white rabbit");
 data().set_other_string_arg("Open your mind");
}; //Вуаля! класс можно передавать по значению, но нельзя скопировать, и реального копирования не происходит



Для того, чтобы понять, что из этого может тебе пригодится, или предложить альтернативное решение, нужно знать твои цели.
Re: Как вернуть NonCopyable объект?
От: T4r4sB Россия  
Дата: 25.12.15 07:52
Оценка:
Здравствуйте, Basil2, Вы писали:

B>MyClass foo()

B>{
B> return MyClass();
B>}
B>[/ccode]

Если этот класс действительно My (мой), то я в таких случаях дописываю нужный конструктор.
Re[2]: Как вернуть NonCopyable объект?
От: Molchalnik  
Дата: 25.12.15 08:25
Оценка:
uzhas, минус-то за что? ты не минусуй, ты словами критикуй
Re[3]: Как вернуть NonCopyable объект?
От: uzhas Ниоткуда  
Дата: 25.12.15 08:34
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>При использовании copy-list-initialization — return {init}; не нужна.


почему не нужна? ничего не смог нагуглить на эту тему =\
Re[4]: Как вернуть NonCopyable объект?
От: Evgeny.Panasyuk Россия  
Дата: 25.12.15 09:07
Оценка:
Здравствуйте, uzhas, Вы писали:

EP>>При использовании copy-list-initialization — return {init}; не нужна.

U>почему не нужна?

Нужно стандарт смотреть.

U>ничего не смог нагуглить на эту тему =\


DEMO
struct Test
{
    Test(const Test&) = delete;
    Test &operator=(const Test&) = delete;
    Test(Test&&) = delete;
    Test &operator=(Test&&) = delete;
    
    Test(int){}
};

Test f()
{
#if 0
    return Test{1}; // compile error
#else
    return {1};
#endif
}

int main()
{
    auto &&x = f();
}
Re[2]: Как вернуть NonCopyable объект?
От: Basil2 Россия https://starostin.msk.ru
Дата: 25.12.15 10:20
Оценка:
Здравствуйте, Molchalnik, Вы писали:

M>Не уверен, что это красиво. Ибо совершается избыточная работа по перемещению.


Тут по сути нет работы, это же не копирование.

M>На мой взгляд, красиво — это либо создать его динамически и вернуть указатель/ссылку (например, через фабрику).


Вариант с unique_ptr рассматривался, но решили не менять сигнатуру функции, ибо потом пришлось бы править ее использование.

Все остальное — имхо настолько оверкилл, что даже дурной тон это использовать.
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Re[3]: Как вернуть NonCopyable объект?
От: Molchalnik  
Дата: 25.12.15 10:56
Оценка:
Здравствуйте, Basil2, Вы писали:

B>Здравствуйте, Molchalnik, Вы писали:


M>>Не уверен, что это красиво. Ибо совершается избыточная работа по перемещению.


B>Тут по сути нет работы, это же не копирование.

Зависит от типа объекта. Если нет копирования — значит, объект лишь оболочка над ссылкой на данные. Т.е. мы имеем вариант со ссылкой, только спрятанный в объект


B>Все остальное — имхо настолько оверкилл, что даже дурной тон это использовать.

смотря для каких целей


B>оверкилл

незнакомое слово. что значит?

B>даже дурной тон это использовать.

Обоснуй?
Re[4]: Как вернуть NonCopyable объект?
От: Basil2 Россия https://starostin.msk.ru
Дата: 25.12.15 11:24
Оценка:
Здравствуйте, Molchalnik, Вы писали:

M>>>Не уверен, что это красиво. Ибо совершается избыточная работа по перемещению.

B>>Тут по сути нет работы, это же не копирование.
M>Зависит от типа объекта. Если нет копирования — значит, объект лишь оболочка над ссылкой на данные. Т.е. мы имеем вариант со ссылкой, только спрятанный в объект

Прочитайте про move-семантику.

B>>Все остальное — имхо настолько оверкилл, что даже дурной тон это использовать.

M>смотря для каких целей
Для заявленных

B>>оверкилл

M>незнакомое слово. что значит?
https://ru.wiktionary.org/wiki/overkill
3) перен. перегиб, крайность; излишество, чрезмерность

B>>даже дурной тон это использовать.

M>Обоснуй?
Зачем делать сложный шаблонный паттерн синглетом, когдо можно не делать его и получить все тоже самое без лишних сущностей и шаблонов?
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Re[5]: Как вернуть NonCopyable объект?
От: Molchalnik  
Дата: 25.12.15 13:58
Оценка: 1 (1) -1 :)
Здравствуйте, Basil2, Вы писали:

B>Прочитайте про move-семантику.

Я знаком с мов-семантикой. И именно поэтому утверждаю, что мов-семантика — это либо скрытый в глубинах объекта указатель, либо лишнее копирование.
Например, если у вас объект стринг, в нём есть указатель на данные, собственно, на массив символов. И тогда копирующий конструктор создаст новый пул памяти длиной со строку и скопирует в него данные из источника. Преимущество мов-семантики в том, что она просто скопирует указатель в новый объект, а старый объект занулит, не удаляя массив символов. Это в разы быстрее. Но что вы будете делать, если ваша мов-семантика столкнётся с объектом — пулом данных без всяких указателей? ничего. Вызовет копирующий конструктор или сдублирует его код в своей имплементации.
именно поэтому мов — семантика НЕ МОЖЕТ БЫТЬ УНИВЕРСАЛЬНЫМ РЕШЕНИЕМ для некопируемых объектов. Потому что негарантировано, что в ОБЩЕМ случае мов-семантика эффективна.

Поэтому для того, чтобы получить точный, хороший ответ на этот вопрос, вы должны описать свойства объектов, для которых вы будете это использовать — любые объекты или лишь некоторые и/или цели, почему вам вдруг понадобилось возвращать некопируемый объект из функции. Тогда можно будет подобрать решение именно под вашу ситуацию.

Заранее хочу опровергнуть рассуждения на тему "так не бывает на практике, поэтому об этом можно не думать". Бывает!!! И чтобы показать это, приведу вам вполне практический пример из жизни — некопируемый объект, для которого неприемлема мов-семантика. т.е. некопируемый и НЕПЕРЕМЕЩАЕМЫЙ объект.

Представим себе, что мы пишем парсер некого формата файла. В этом случае часто получается, что у нас в памяти оказывается пул данных (длинна пула данных неизвестна на этапе компиляции), но при этом начало этого файла — заголовок заранее известной длинны и с известными полями. В этом случае довольно часто можно увидеть следующий код:
class FileHandle;
extern char * ReadFile(FileHandle, size_t number_bytes_to_read);

#pragma pack(push,1)
class FieldHeader {
 public:
  typedef unsigned char Byte;
  enum {kHeaderSize = некое число};

  void set_header_field1(Field1Type& value) {field1_ = value;}
  void set_header_field2(Field2Type& value) {field2_ = value;}
  void set_header_field3(Field3Type& value) {field3_ = value;}
  const Field1Type& get_header_field1() const {return field1_;}
  const Field2Type& get_header_field2() const {return field2_;}
  const Field3Type& get_header_field3() const {return field3_;}
  size_t GetDataSize() const {return data_size_;}
  Byte GetByteAt(size_t position) {return data[position + kHeaderSize];}
  unsigned GetUnsignedAt(size_t position) {return * static_cast<unsigned *>(data+position + kHeaderSize);}
  template <typename T> T* GetAt(size_t position) {return static_cast<T *>(data+position + kHeaderSize);}

  static SmartPointer<FieldHeader> ReadFromFile(FileHandle file, size_t block_size) {
    SmartPointer<FieldHeader> memory_pool = SmartPointer<FieldHeader>( Fabrique::Allocate<Byte>(block_size) );
    ReadFile( memory_pool.data(), block_size - kHeaderSize );
    memory_pool->data_size_ = block_size - kHeaderSize;
    return memory_pool;
  }

 private:
  size_t data_size_;
  Field1Type field1_;
  Field2Type field2_;
  Field3Type field3_;
  Byte data[0]; //работает не на всех компиляторах. В некоторых случаях эту строку нужно заменить на Byte data[1];
}
#pragma pack(pop)


Прошу обратить внимание на идиому Byte data[0];
Это классический трюк для работы с объектом, чья длина известна только во время исполнения кода.
При этом данный объект является классическим некопируемым объектом — просто потому, что копировать его не имеет смысла, а если это произойдёт, это может привести к печальным для производительности последствиям — ведь его длина может достигать полумегабайта, например. поэтому имеет смысл делать его некопируемым, а передавать на него именно указатель. В данном случае, кстати, синглтон ни к селу, ни к городу. Но это не означает, что синглтон перегиб — просто синглтон перегиб для ДАННОГО случая, и , по-видимому, и для вашего.

B>>>Все остальное — имхо настолько оверкилл, что даже дурной тон это использовать.

M>>смотря для каких целей
B>Для заявленных
Имхо: заявлены лишь средства. Хочу возвращать non-copyable объект. Для чего возвращать — это и есть цель. О ней умолчали.

B>3) перен. перегиб, крайность; излишество, чрезмерность

Спасибо. Лично мне русское слово "перегиб" нравится больше. Меньше понтов, больше смысла, патриотично. Но буду знать теперь, what is it

B>>>даже дурной тон это использовать.

M>>Обоснуй?
B>Зачем делать сложный шаблонный паттерн синглетом, когдо можно не делать его и получить все тоже самое без лишних сущностей и шаблонов?
Я привёл выше пример, когда нельзя получить всё тоже самое с семантикой перемещений (мов-семантикой). Так же можно придумать и практически существующую задачу, где синглтон будет удобнее всего. А синглтон не нужно реализовывать — он должен быть "всегда с собой" — реализован один раз и сохранён в библиотеке.

Резюме: для вашего случая, я согласен, предложенные рецепты — перегиб. Но существуют ситуации, когда данные рецепты перегибом не будут, а будут красивым и органичным решением, и чтобы понять, что хорошо для вашего случая — нужно получить больше информации о том, для каких объектов поставлена задача, и для каких целей вам нужно возвращать некопируемый объект.
Re: Как вернуть NonCopyable объект?
От: ononim  
Дата: 25.12.15 14:57
Оценка:
B>Условную компиляцию и отказ от noncopyable не предлагать
А если на принимающей стороне принимать в ссылку:
const MyClass &r =  foo();

? (самому лень проверять)
ЗЫ Работоспособность этой конструкции (именно с const) гарантируется стандартом.
Как много веселых ребят, и все делают велосипед...
Re[2]: Как вернуть NonCopyable объект?
От: Evgeny.Panasyuk Россия  
Дата: 25.12.15 17:59
Оценка:
Здравствуйте, ononim, Вы писали:

B>>Условную компиляцию и отказ от noncopyable не предлагать

O>А если на принимающей стороне принимать в ссылку:
O>
O>const MyClass &r =  foo();
O>

O>? (самому лень проверять)

Всё равно нужен конструктор копирования/перемещения, если только внутри не используется copy-list-initialization.
Эта const& убирает только одно (внешнее) копирование/перемещение из двух.

O>ЗЫ Работоспособность этой конструкции (именно с const) гарантируется стандартом.


Есть ещё auto&&.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.