Можно ли позже создать элементы std::tuple с помощью распределителя?

Насколько я понял, одной из причин использования распределителей C++ для моего собственного контейнера было бы то, что я могу разделить выделение и построение.

Теперь мне интересно, возможно ли это для std::tuples следующим образом: каждый раз, когда я создаю std::tuple, пространство резервируется, но объекты не создаются (пока). Вместо этого я могу использовать распределитель для создания i-го аргумента именно тогда, когда захочу.

Псевдокод:

struct my_struct {
    const bool b; // note that we can use const
    my_struct(int x) : b(x==42) {}
};

int main()
{
    std::tuple<int, my_struct> t;
    // the tuple knows an allocator named my_allocator here
    // this allocator will force the stack to reserve space for t,
    // but the contained objects are not constructed yet.

    my_allocator.construct(std::get<0>(t), 42);
    // this line just constructed the first object, which was an int
    my_allocator.construct(std::get<1>(t), std::get<0>(t));
    // this line just constructed the 2nd object
    // (with help of the 1st one

    return 0;
}

Одна из возможных проблем заключается в том, что распределители обычно привязаны к типу, поэтому мне нужен один распределитель для каждого типа. Другой вопрос заключается в том, должна ли память для std::tuple выделяться в куче или может работать стек. И то, и другое для меня нормально.

Тем не менее, это возможно как-то? Или, если нет, можно ли это сделать с помощью распределителя, который я напишу сам?


person Johannes    schedule 23.12.2013    source источник
comment
Попробуйте std::get_temporary_buffer вместе с неинициализированными алгоритмами хранения.   -  person Kerrek SB    schedule 24.12.2013
comment
Вот некоторый доказательство концептуального кода, который демонстрирует, как я могу это сделать, но я абсолютно не знаю, допустим ли это C++ .   -  person Kerrek SB    schedule 24.12.2013
comment
@KerrekSB: предварительное условие любой стандартной библиотечной функции, вызываемой с объектом или ссылкой/указателем на такой объект, заключается в том, что объект существует (если явно не указано иное). Ясно, что в неинициализированной памяти нет объекта.   -  person Dietmar Kühl    schedule 24.12.2013
comment
@DietmarKühl: я надеялся, что какой-то &* не оценивает магию операнда ... Я также рассматривал указатели на члены, но не знал, как получить один из них для кортежей (вероятно, базовые указатели были бы правильными в тот случай).   -  person Kerrek SB    schedule 24.12.2013
comment
@KerrekSB Не уверен, может быть, это то, что я хотел. Что означает строка кода new? Какой версии этого документа он соответствует? Я так понимаю версия 3?   -  person Johannes    schedule 24.12.2013


Ответы (1)


Распределители не помогут вам с инициализацией объектов: роль распределителя заключается в предоставлении необработанной, т. е. неинициализированной памяти. Распределитель можно использовать с std::tuple<...>, чтобы настроить, как, например, выделяется память для std::string или std::vector<...>.

Если вы хотите отложить создание объектов, вам нужно будет использовать что-то вроде «необязательного» объекта, который будет указывать с помощью флага, что он еще не построен. Стратегия реализации для соответствующего класса будет оберткой вокруг подходящего union.

person Dietmar Kühl    schedule 23.12.2013
comment
Спасибо, а можно поподробнее? При чем тут союз (а может и вариант)? Обратите внимание, что я не хочу инициализировать только один элемент кортежа, а может быть несколько, и они должны жить одновременно. - person Johannes; 24.12.2013
comment
Кроме того, для аргумента необработанной памяти: поможет ли вам выделить пространство std::tuple в куче? - person Johannes; 24.12.2013
comment
@Johannes: union - это единственный способ переносимого внедрения еще не созданного объекта в другой объект (например, массив того же размера может не работать из-за проблем с инициализацией). Если несколько элементов идут вместе, вы можете поместить их во вложенную std::tuple<...> (и, возможно, использовать пользовательскую функцию get<...>(), которая считает их невложенными). - person Dietmar Kühl; 24.12.2013
comment
@Johannes: мертвый объект - это мертвый объект. Неважно, где объект не создан: пока он не создан, вы не можете использовать его [переносимо]. - person Dietmar Kühl; 24.12.2013
comment
@Johannes: В Википедии есть пример использования unions для построения разные объекты в одном месте, но должно быть ясно, как то же самое можно сделать, чтобы вместо этого использовать необязательно сконструированный объект. - person Dietmar Kühl; 24.12.2013
comment
Под портативностью вы подразумеваете, что многие функциональные возможности std::tuple небезопасны до тех пор, пока объект кортежа не будет полностью построен? Хорошо, верно, но если я получу доступ только к элементам кортежа, из которых я знаю, что они созданы? Тогда почему это не должно работать? - person Johannes; 24.12.2013
comment
@Johannes: под переносимостью я подразумеваю то, что стандарт гарантирует определенное поведение. При путешествии с неопределенным поведением может показаться, что все работает, или системы могут даже предоставлять гарантии, но они не будут применяться везде. Соответствующий раздел стандарта — 3.8 [basic.life], особенно параграф 5. Вам может понравиться неопределенное поведение этой страницы. - person Dietmar Kühl; 24.12.2013