Se eu passar o código a seguir por meio de meu instantâneo GCC 4.7, ele tentará copiar os unique_ptr
s para o vetor.
#include <vector>
#include <memory>
int main() {
using move_only = std::unique_ptr<int>;
std::vector<move_only> v { move_only(), move_only(), move_only() };
}
Obviamente, isso não pode funcionar porque std::unique_ptr
não é copiável:
erro: uso da função excluída 'std :: unique_ptr <_Tp, _Dp> :: unique_ptr (const std :: unique_ptr <_Tp, _Dp> &) [com _Tp = int; _Dp = std :: default_delete; std :: unique_ptr <_Tp, _Dp> = std :: unique_ptr] '
O GCC está correto ao tentar copiar os ponteiros da lista de inicializadores?
c++
c++11
initializer-list
move-semantics
R. Martinho Fernandes
fonte
fonte
Respostas:
A sinopse de
<initializer_list>
em 18.9 deixa razoavelmente claro que os elementos de uma lista de inicializadores são sempre passados por referência const. Infelizmente, não parece haver nenhuma maneira de usar a semântica de movimento nos elementos da lista de inicializadores na revisão atual da linguagem.Especificamente, temos:
fonte
const
, que não podem ser descartados em um programa bem formado.Edit: Já que @Johannes não parece querer postar a melhor solução como uma resposta, eu simplesmente farei isso.
Os iteradores retornados por
std::make_move_iterator
moverão o elemento apontado ao serem desreferenciados.Resposta original: vamos utilizar um pequeno tipo auxiliar aqui:
Infelizmente, o código direto aqui não funcionará:
Já que o padrão, por qualquer motivo, não define um construtor de cópia de conversão como este:
O
initializer_list<rref_wrapper<move_only>>
criado por brace-init-list ({...}
) não será convertido para oinitializer_list<move_only>
quevector<move_only>
leva. Portanto, precisamos de uma inicialização em duas etapas aqui:fonte
std::ref
, não? Talvez deva ser chamadostd::rref
.move_only m[] = { move_only(), move_only(), move_only() }; std::vector<move_only> v(std::make_move_iterator(m), std::make_move_iterator(m + 3));
.move_iterator
ainda.std::distance
para iteradores avançados ou melhores estd::move_iterator
adapta a categoria do iterador subjacente. De qualquer forma, solução boa e concisa. Postá-lo como uma resposta, talvez?Como mencionado em outras respostas, o comportamento do
std::initializer_list
é segurar objetos por valor e não permitir movimentação para fora, então isso não é possível. Aqui está uma possível solução alternativa, usando uma chamada de função em que os inicializadores são fornecidos como argumentos variáveis:Infelizmente
multi_emplace(foos, {});
falha, pois não pode deduzir o tipo de{}
, portanto, para que os objetos sejam construídos por padrão, você deve repetir o nome da classe. (ou usarvector::resize
)fonte
Usando truque de Johannes Schaub
std::make_move_iterator()
comstd::experimental::make_array()
, você pode usar uma função auxiliar:Veja ao vivo Coliru.
Talvez alguém possa alavancar
std::make_array()
o truque para permitirmake_vector()
fazer suas coisas diretamente, mas não vi como (mais precisamente, tentei o que achei que deveria funcionar, falhei e segui em frente). Em qualquer caso, o compilador deve ser capaz de embutir a matriz na transformação do vetor, como o Clang faz com o O2 ativado GodBolt.fonte
Como foi apontado, não é possível inicializar um vetor do tipo move-only com uma lista de inicializadores. A solução proposta originalmente por @Johannes funciona bem, mas eu tenho outra ideia ... E se nós não criarmos um array temporário e então movermos os elementos de lá para o vetor, mas usarmos posicionamento
new
para inicializar este array já no lugar do bloco de memória do vetor?Esta é minha função para inicializar um vetor de
unique_ptr
's usando um pacote de argumentos:fonte
result.data()
não é um ponteiro para alguma memória aleatória. É um ponteiro para um objeto . Pense no que acontece com aquele pobre objeto quando você coloca um novo sobre ele.