cpp-ru / ideas

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

Добавить тэг std::aggregate_t и использующие его фабричные функции #392

Closed apolukhin closed 3 years ago

apolukhin commented 3 years ago

Перенос предложения: голоса +7, -0 Автор идеи: Олег Фатхиев

Часто бывает нужно создать объект через aggregate initialization или list initialization. Когда речь идет о объектах в куче, мы используем std::make_unique или std::make_shared. Однако они не могут вызвать list или aggregate initialization (и правильно делают). Предлагаю добавить тэг std::aggregate, который будет говорить о том, что объект нужно создать с помощью aggregate или list initialization.

Бывает нужно, чтобы работал такой код:

struct some_aggregate_t {
    int i;
    std::string s;
};

auto value = std::make_unique<some_aggregate_t>(5, "lol"); // error: no matching constructor for initialization of 'some_aggregate_t'

std::make_unique пытается вызвать конструктор у some_aggregate_t, вместо aggregate initialization. Поведение ожидаемое и понятное, но хочется уметь создавать такие объекты без необоходимости дописывать конструктор.

Еще один пример:

auto v = std::make_shared<std::vector<int>>(5, 2);
assert(*v == std::vector<int>{2, 2, 2, 2, 2});

В данном коде создается вектор с 5-ью 2-ками.

Предлагается добавить tag type std::aggregate_t, позволяющий специализировать вызов std::make_unique и std::make_shared:

auto valid = std::make_unique(std::aggregate, 5, "lol"); // ok

auto v = std::make_shared<std::vector<int>>(std::aggregate, 5, 2);
assert(*v == std::vector<int>{5, 2});

Возможная реализация std::make_unique:

template <class T, class... Args>
std::unique_ptr<T> make_unique(std::aggregate_t, Args&&... args) {
    return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}
apolukhin commented 3 years ago

Andrey Davydov, 19 декабря 2018, 19:10 Кажется, что принятый в C++20 proposal http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0960r1.html делает ненужным Ваше предлажение.

Олег Фатхиев, 19 декабря 2018, 20:36 Andrey Davydov, действительно, это решает одну из проблем. Вызов list initializing это не чинит.

Andrey Davydov, 20 декабря 2018, 8:58 Олег Фатхиев, что Вы имеете в виду? Какой из Ваших примеров это не чинит?

Если что, std::vector считается ненормальным, классы для которых, X(2, 5) и X{2, 5} означают разное считаются ошибкой природы, и ради них создавать новый механизм (std::aggregate) это черезчур.

Олег Фатхиев, 20 декабря 2018, 15:15 Andrey Davydov, получается, что любой класс, имеющий конструктор от std::initializer_list, ненормальный?

Andrey Davydov, 20 декабря 2018, 15:35 Олег Фатхиев, ок, т.е. речь идет именно о классах конструирующихся от std::initializer_list? Для них есть неоднозначность, которую можно решать, предложенным Вами способом (только имя std::aggregate будет не очень удачным), а можно и не решать, так ли хуже

std::make_shared<std::vector<int>>(std::initializer_list{5, 2})

чем предлагаемый Вами

std::make_shared<std::vector<int>>(std::aggregate, 5, 2)

?

Теперь по поводу "ненормальных" классов. Я, наверное, резко выразился, но ситуация когда для класса {} и () инициализация приводит к разным результатам действительно крайне специфичная. Она же встречается не для любого вектора и любых аргументов, а только вектора целых чисел, конструирующегося от 1-2 аргументов. И да, я думаю, что возникновение подобных ситуацией это проблема в дизайне std::vector.

Анатолий Томилов, 26 декабря 2018, 15:28 Andrey Davydov,

std::initializer_list -- это ведь обязательное поэлементное копирование (именно копирование и именно неизбежное).

Andrey Davydov, 26 декабря 2018, 15:44 Анатолий Томилов, да, и что? Мы же все равно собираемся создавать std::initializer_list и передавать его в конструктор нашего класса, на важно в какой момент это делать -- внутри функции make_shared или снаружи от нее.

Antervis, 21 декабря 2018, 11:13 во-первых, лучше сразу ориентироваться на std::in_place_t, решающий именно такую задачу для variant/any/optional. Во-вторых, наверно, лучше сделать не только перегрузку make_unique/make_shared, а также перегрузки конструкторов unique_ptr/shared_ptr. По крайней мере с появлением deduction guides потребность в make_фунциях сильно снизилась.

yndx-antoshkka, 26 февраля 2019, 15:24 P0960 приняли в C++20. В std::aggregate_t смысл отпал.