cpp-ru / ideas

Идеи по улучшению языка C++ для обсуждения
https://cpp-ru.github.io/proposals
Creative Commons Zero v1.0 Universal
89 stars 0 forks source link

Возможность изпользовать std::make_shared с приватными конструкторами и фабричными методами #215

Open apolukhin opened 3 years ago

apolukhin commented 3 years ago

Перенос предложения: голоса +3, -5 Автор идеи: fgenatre

В нынешнем виде std::make_shared<>() не позволяет конструировать объекты с приватным конструктором даже при вызове из подходящего контекста (разве что только с некоторыми ухищрениями, типа наследования пустого производного класса и создания его экземпляра или объявлении вызываемой функции как friend для создаваемого класса). Между тем, на текущий момент не видно каких-либо серьезных преград разрешить использование std::make_shared<>() для объектов с приватными конструкторами.

std::make_shared<>() имеет ряд преимуществ перед обычным конструированием std::shared_ptr - к примеру, память и выделяется за один раз, а не за два (согласно рекомендации в стандарте), и сам объект и счетчик ссылок на него распалагаются в куче рядом (что в некоторых случаях может дать небольшой плюс в плане оптимизации). В нынешнем виде std::make_shared<>() не позволяет конструировать объекты с приватным конструктором даже при вызове из подходящего контекста (разве что только с некоторыми ухищрениями, типа наследования пустого производного класса и создания его экземпляра или объявлении вызываемой функции как friend для создаваемого класса). Между тем, на текущий момент не видно каких-либо серьезных преград разрешить использование std::make_shared<>() для объектов с приватными конструкторами - компилятору понадобится просто определить допустимость подобной операции в зависимости от контекста вызова (как сейчас при обычном создании shared_ptr).

Бонусом можно еще разрешить использовать make_shared<>() с фабричными методами, возвращающими сырой указатель (типа SomeObject* SomeObject::create(), например при использовании сторонних библиотек) - только там заранее неизвестно какой объект вернется (может быть сконструирован объект одного из унаследованных классов), следовательно, заранее память не аллоцируешь, и как вариант, компилятор может генерировать вариант фабричного метода, в котором вместо 'new' будет вызываться make_shared, и сконструированный умный указатель будет передаваться в вызываюшую функцию

Плюсы: упрощение кода и улучшение читаемости.

apolukhin commented 3 years ago

Павел, 7 августа 2017, 17:01 На данный момент, если написать make_shared (где C -- класс с приватным конструктором), мы получим от компилятора стек из восьми функций (на моей версии libstdc++), вызываемых внутри make_shared. Верхняя функция -- __gnu_cxx::new_allocator::construct. Должно ли быть у неё разрешение на вызов приватного конструктора C? А у остальных семи промежуточных функций?

Как ваше предложение могло бы быть реализованно в стандарте и в компиляторах? Мне абсолютно не понятен механизм :(

Antervis, 15 августа 2017, 14:03 можно добавить специализацию make_shared как friend

yndx-antoshkka, 15 августа 2017, 14:24 Antervis, не поможет. Нужно ещё добавить allocate_shared и возможно какие-то внутренности конкретной имплементации стандартной библиотеки.

Дмитрий, 16 августа 2017, 9:50 Можно просто сконструировать объект снаружи и засунуть его как rvalue в make_shared. В C++17 при этом не должно произойти ни копирования, ни переноса.

yndx-antoshkka, 16 августа 2017, 13:32 dmitriy@izvolov.ru, вы подняли очень интересную тему! В данный момент так не работает https://godbolt.org/g/V3edfK . Если сделать чтобы работало - будет ооочень круто, rvalue будут более легковесными и будут работать с copy elision.

Однако это поменяет порядок следования вызовов. И может быть неприятно в ряде случаев. Я обдумаю и обсужу с разработчиками компиляторов.

Дмитрий, 16 августа 2017, 14:57 yndx-antoshkka, действительно, как-то я слишком оптимистично отнёсся к "copy elision". Но с интересом жду результатов обдумывания и обсуждения.