История такова: существует пул памяти фиксированного типа Pool, в котором хранятся элементы некоторого типа T. Обе проблемы, перечисленные в заголовке, были обнаружены при создании функции alloc(), которая создает и добавляет новый элемент в пул:

template <class T, size_t qty, class Alloc = allocator<T>>
class Pool {
    array <T*, qty> cells; // Pointers to pre-allocated memory
    ...
public:
    T& alloc (...) {   // [2] It is unknown what parameters T's constructor may take
        T&& tmp (...); // [2] But I need them to be passed as they are
        size_t cellNo = findEmptyCell(); // Returns the number of the cell
        *cells[cellNo] = tmp; // Placing the new object into the pool
                              // [1] "invalid conversion from 'int&& (*)(...)' to 'int'" when T is int
        isEmpty[cellNo] = false; // Marking the cell as occupied
        return *cells[cellNo];
    }
}

Итак, 1) как избежать копирования ненужных объектов в этом случае?
И 2) есть ли способ передать конструктору произвольные аргументы?

0
Alex 22 Фев 2016 в 19:53
1
T&& tmp (...); Это объявление функции. Я предполагаю, что вы имели в виду что-то другое и использовали неправильный синтаксис. Кроме того, если я не ошибаюсь, ваш пул памяти не заполнен заранее созданными объектами, поэтому *cells[cellNo] = tmp должен быть UB, если тип не POD. Просто используйте новое размещение.
 – 
Weak to Enuma Elish
22 Фев 2016 в 20:00
Да, я предполагал передать аргументы alloc() в T(), спасибо за объяснение ошибки [1]. Но почему *cells[cellNo] = tmp должен генерировать UB? cells указывают на предварительно выделенную память, поэтому следует просто использовать ее. Или вы имеете в виду, что это вызов для operator=, который не определен для мусора, изначально находящегося в *cells[cellNo]?
 – 
Alex
22 Фев 2016 в 20:11
1
Он вызовет operator=. Если вы уже выделили память и создали там объект, он будет работать должным образом. Если объект не построен и не является POD, это вызовет UB. И, конечно, если вы не выделили память, она эффектно взорвется.
 – 
Weak to Enuma Elish
22 Фев 2016 в 20:16

1 ответ

Лучший ответ

Вы ищете "идеальную пересылку" с помощью шаблона функции с переменным числом аргументов:

template <class... Args>
T& alloc(Args&&... args) { 
    size_t cellNo = findEmptyCell();
    *cells[cellNo] = T(std::forward<Args>(args)...);
    isEmpty[cellNo] = false;
    return *cells[cellNo];
}

Это будет принимать любое количество аргументов и пересылать их (копировать для lvalues, перемещать для rvalues) в конструктор T. Затем этому временному объекту будет назначено перемещение в *cells[cellNo].

2
Barry 22 Фев 2016 в 19:57
Есть ли разница между *cells[cellNo] = T(std::forward<Args>(args)...); и new (cells[cellNo]) T(forward<Args>(args)...); в этом случае?
 – 
Alex
23 Фев 2016 в 08:55
Да, для этого указателя есть старый объект? Если это так, назначение будет работать правильно, но новое размещение не разрушит старое. Если нет старого объекта, то присвоение неверно.
 – 
Barry
23 Фев 2016 в 16:10