Por que não há std::make_unique
modelo de função na biblioteca C ++ 11 padrão? eu acho
std::unique_ptr<SomeUserDefinedType> p(new SomeUserDefinedType(1, 2, 3));
um pouco detalhado. O seguinte não seria muito melhor?
auto p = std::make_unique<SomeUserDefinedType>(1, 2, 3);
Isso oculta new
bem e apenas menciona o tipo uma vez.
De qualquer forma, aqui está minha tentativa de implementação de make_unique
:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
Levei um bom tempo para std::forward
compilar o material, mas não tenho certeza se está correto. É isso? O que exatamente std::forward<Args>(args)...
significa? O que o compilador faz disso?
c++
c++11
variadic-templates
unique-ptr
perfect-forwarding
fredoverflow
fonte
fonte
unique_ptr
necessário um segundo parâmetro de modelo que você deve permitir de alguma forma - que é diferenteshared_ptr
.make_unique
com um deleter costume, porque, obviamente, ele aloca via velha e simplesnew
e, portanto, deve usar planície antigadelete
:)make_unique
seria limitada ànew
alocação ... bem, tudo bem se você quiser escrever, mas posso ver por que algo assim não faz parte do padrão.make_unique
modelo, pois o construtor destd::unique_ptr
é explícito e, portanto, é detalhado retornarunique_ptr
de uma função. Além disso, eu prefiro usar doauto p = make_unique<foo>(bar, baz)
questd::unique_ptr<foo> p(new foo(bar, baz))
.make_unique
está chegandoC++14
, consulte isocpp.org/blog/2013/04/trip-report-iso-c-spring-2013-meetingRespostas:
Herb Sutter, presidente do comitê de padronização C ++, escreve em seu blog :
Ele também fornece uma implementação idêntica à fornecida pelo OP.
Edit:
std::make_unique
agora faz parte do C ++ 14 .fonte
make_unique
modelo de função não garante, por si só, chamadas seguras de exceção. Ele depende da convenção de que o chamador a usa. Por outro lado, a verificação estrita de tipo estático (que é o principal diferencial entre C ++ e C) baseia-se na ideia de impor segurança, por meio de tipos. E, para isso,make_unique
pode ser simplesmente uma classe em vez de uma função. Por exemplo, consulte o artigo do meu blog em maio de 2010. Também está vinculado à discussão no blog da Herb.make_unique
uma classe, agora acho que é melhor transformá-la em uma função que produzamake_unique_t
, a razão disso é um problema com a análise mais perversa :-).make_unique
oferece uma forte garantia de exceção. Ou talvez você esteja misturando a ferramenta ao seu uso, caso em que nenhuma função é segura para exceções. Considerevoid f( int *, int* ){}
, oferece claramente ano throw
garantia, mas, por sua linha de raciocínio, não é uma exceção segura, pois pode ser mal utilizada. Pior,void f( int, int ) {}
também não é exceção segura !:typedef unique_ptr<int> up; f( *up(new int(5)), *up(new int(10)))
...make_unique
implementado como acima e você me apontar para um artigo de Sutter, o artigo que meu google-fu me apontou para estados quemake_unique
oferece a garantia forte exceção , o que contradiz a sua declaração acima. Se você tem um artigo diferente Eu estou interessado em lê-lo. Portanto, minha pergunta original é: Comomake_unique
(como definido acima) não é exceção segura? (Sidenote: sim, eu acho quemake_unique
melhora a segurança de exceção em lugares onde ele pode ser aplicado)make_unique
. Primeiro, não vejo como este é inseguro e não vejo como adicionar um tipo extra o tornaria mais seguro . O que sei é que estou interessado em entender os problemas que esta implementação pode ter - não consigo ver nenhum - e como uma implementação alternativa os resolveria. Quais são as convenções quemake_unique
dependem? Como você usaria a verificação de tipo para reforçar a segurança? Essas são as duas perguntas para as quais eu adoraria uma resposta.Bom, mas Stephan T. Lavavej (mais conhecido como STL) tem uma solução melhor para
make_unique
, que funciona corretamente para a versão do array.Isso pode ser visto em seu vídeo Core C ++ 6 .
Uma versão atualizada da versão do make_unique da STL está agora disponível como N3656 . Esta versão foi adotada no rascunho C ++ 14.
fonte
make_unique
deve ir em um cabeçalho. Os cabeçalhos não devem importar um espaço para nome (consulte o Item # 59 no livro "Padrões de codificação C ++" de Sutter / Alexandrescu). As mudanças do Xeo ajudam a evitar o incentivo a práticas ruins.std::make_shared
não é apenas uma abreviação parastd::shared_ptr<Type> ptr(new Type(...));
. Faz algo que você não pode fazer sem ele.Para fazer seu trabalho, é
std::shared_ptr
necessário alocar um bloco de rastreamento, além de reter o armazenamento para o ponteiro real. No entanto, comostd::make_shared
aloca o objeto real, é possível questd::make_shared
aloque o objeto e o bloco de rastreamento no mesmo bloco de memória.Portanto, embora
std::shared_ptr<Type> ptr = new Type(...);
houvesse duas alocações de memória (uma para anew
, uma nostd::shared_ptr
bloco de rastreamento),std::make_shared<Type>(...)
alocaria um bloco de memória.Isso é importante para muitos usuários em potencial de
std::shared_ptr
. A única coisa que umstd::make_unique
faria seria ser um pouco mais conveniente. Nada além disso.fonte
Embora nada impeça você de escrever seu próprio ajudante, acredito que o principal motivo para fornecer
make_shared<T>
na biblioteca é que ele realmente cria um tipo interno de ponteiro compartilhado diferente doshared_ptr<T>(new T)
que é alocado de maneira diferente, e não há como conseguir isso sem o dedicado ajudante.SeuCorreção: isso não é verdade: ter uma chamada de função para quebrar amake_unique
invólucro, por outro lado, é mero açúcar sintático em torno de umanew
expressão; portanto, embora pareça agradável aos olhos, não traz nadanew
para a mesa.new
expressão fornece segurança de exceção, por exemplo, no caso em que você chama uma funçãovoid f(std::unique_ptr<A> &&, std::unique_ptr<B> &&)
. Ter doisnew
s brutos que não são sequenciais um em relação ao outro significa que, se uma nova expressão falhar com uma exceção, a outra poderá vazar recursos. Quanto ao porquê não existemake_unique
no padrão: foi apenas esquecido. (Isso acontece ocasionalmente. Também não há globalstd::cbegin
no padrão, mesmo que deva haver um.)Observe também que
unique_ptr
utiliza um segundo parâmetro de modelo que você deve permitir de alguma forma; é diferente deshared_ptr
, que usa apagamento de tipo para armazenar deleters personalizados sem torná-los parte do tipo.fonte
shared_ptr<T>(new T)
usa um deles,make_shared<T>()
usa um diferente. Permitir que isso seja uma coisa boa, e a versão make-shared é, em certo sentido, o ponteiro compartilhado mais leve que você pode obter.shared_ptr
aloca um bloco de memória dinâmica para manter a contagem e a ação "eliminador" quando você cria umshared_ptr
. Se você passar o ponteiro explicitamente, ele precisará criar um bloco "novo"; se você usá-make_shared
lo, poderá agrupar seu objeto e os dados do satélite em um único bloco de memória (umnew
) resultando em alocação / desalocação mais rápida, menos fragmentação e (normalmente ) melhor comportamento de cache.No C ++ 11
...
é usado (no código do modelo) também para "expansão de pacote".O requisito é que você o utilize como sufixo de uma expressão que contenha um pacote de parâmetros não expandido, e ele simplesmente aplicará a expressão a cada um dos elementos do pacote.
Por exemplo, desenvolvendo seu exemplo:
O último está incorreto, eu acho.
Além disso, o pacote de argumentos não pode ser passado para uma função não expandida. Não tenho certeza sobre um pacote de parâmetros de modelo.
fonte
std::forward<Args>(args)...
, que se expande paraforward<T1>(x1), forward<T2>(x2), ...
.forward
realmente sempre exige um parâmetro de modelo, não é?Inspirado na implementação de Stephan T. Lavavej, achei que seria bom ter um make_unique que suporta extensões de matriz, está no github e eu gostaria de receber comentários sobre ele. Ele permite que você faça isso:
fonte