Qualquer risco de mover elementos const_cast para fora de um std :: initializer_list?
Esta questão se baseia nessa@ Pergunta de FredOverflow.
ESCLARECIMENTO: initializer_list
abordagem é necessária como oVC ++ 2012 tem um bug o impede a expansão encaminhada de argumentos com espaços para nome._MSC_VER <= 1700
tem o bug.
Eu escrevi uma função de modelo variadic que recolhe qualquer número de argumentos em um recipiente digitado. Eu uso o construtor do tipo para converter os argumentos variádicos em valores consumíveis. Por exemplo._variant_t
:)
Eu preciso disso para o meuMySql
Biblioteca C ++ ao enviar argumentos para instruções preparadas em um único golpe, enquantoMySqlVariant
converte os dados de entrada paraMYSQL_BIND
s. Como eu posso trabalhar comBLOB
s, eu gostaria de evitar o copy-construct tanto quanto possível quando pudermove&&
os grandes recipientes ao redor.
Eu fiz um teste simples e notei que oinitialize_list
fazcopy-construct
para os elementos armazenados e os destrói quando sai do escopo. Perfeito ... Então eu tentei mover os dados para fora doinitializer_list
e, para minha surpresa, usoulvalues
nãorvalues
como eu esperava comstd::move
.
Engraçado como isso acontece logo depoisIndo em 2013 nativo claramente me avisou quemove não se move, forward não avança...ser como a minha amiga - para ficar no fundo do pensamento.
Mas isso não me impediu :) eu decidi queconst_cast
ainitializer_list
valores e ainda movê-los para fora. Uma ordem de despejo precisa ser aplicada. E isso éminha implementação:
template <typename Output_t, typename ...Input_t>
inline Output_t& Compact(Output_t& aOutput, Input_t&& ...aInput){
// should I do this? makes sense...
if(!sizeof...(aInput)){
return aOutput;
}
// I like typedefs as they shorten the code :)
typedef Output_t::value_type Type_t;
// can be either lvalues or rvalues in the initializer_list when it's populated.
std::initializer_list<Type_t> vInput = { std::forward<Input_t>(aInput)... };
// now move the initializer_list into the vector.
aOutput.reserve(aOutput.size() + vInput.size());
for(auto vIter(vInput.begin()), vEnd(vInput.end()); vIter != vEnd; ++vIter){
// move (don't copy) out the lvalue or rvalue out of the initializer_list.
// aOutput.emplace_back(std::move(const_cast<Type_t&>(*vIter))); // <- BAD!
// the answer points out that the above is undefined so, use the below
aOutput.emplace_back(*vIter); // <- THIS is STANDARD LEGAL (copy ctor)!
}
// done! :)
return aOutput;
}
Usando isso é fácil:
// You need to pre-declare the container as you could use a vector or a list...
// as long as .emplace_back is on duty!
std::vector<MySqlVariant> vParams;
Compact(vParams, 1, 1.5, 1.6F, "string", L"wstring",
std::move(aBlob), aSystemTime); // MySql params :)
Eu também fiz o upload de um teste completo na IDEone ^ que mostra como a memória de umstd::string
move-se adequadamente com esta função.(Eu colaria tudo aqui, mas é um pouco longo ...)
Contanto que o_variant_t
(ou qualquer objeto de embrulho final) tem os construtores certos, é ótimo. E se os dados puderem ser removidos, é ainda melhor. E funciona muito bem como eu testei e coisasstd::move
na direção certa :)
Minhas perguntas são simples:
Estou fazendo isso corretamente?O fato de estar funcionando corretamente ou apenas um efeito colateral?E sestd::move
não funciona por padrão eminitializer_list
é o que estou fazendo aqui:ilegal, imoral, hacky ... ou simplesmente errado?PS: Sou autodidataWindows Native C++
desenvolvedor, ignorante dos padrões.
^ minha desculpa se estou fazendo coisas realmente não-padrão aqui.
ATUALIZAR
Obrigado a todos, tenho a resposta e a solução(um curto e longo) agora.
E eu amo o lado C ++ 11 do SO. Muitas pessoas conhecedoras aqui ...