Quais idiomas C ++ foram descontinuados no C ++ 11?

192

Com o novo padrão, existem novas maneiras de fazer as coisas, e muitas são mais agradáveis ​​que as antigas, mas a antiga ainda é boa. Também está claro que o novo padrão não obsoleta muito, por motivos de compatibilidade com versões anteriores. Portanto, a questão que resta é:

Quais formas antigas de codificação são definitivamente inferiores aos estilos C ++ 11 e o que podemos fazer agora?

Ao responder isso, você pode pular as coisas óbvias como "usar variáveis ​​automáticas".

Alan Baljeu
fonte
13
Você não pode descontinuar expressões idiomáticas.
Pubby
6
A palestra de Herb Sutter no Going Native 2012 abordou isso:
bames53 15/02
5
Retornar valores constantes não é mais incentivado. Obviamente auto_ptr, também está obsoleto.
Kerrek SB /
27
Claro que você pode, Pubby. Antes da criação de modelos C ++, havia uma técnica de macro para fazer modelos. Em seguida, o C ++ os adicionou e a maneira antiga era considerada ruim.
Alan Baljeu 15/02/12
7
Esta questão realmente precisa ser movida para Programmers.se.
Nicol Bolas

Respostas:

173
  1. Classe Final : C ++ 11 fornece o finalespecificador para impedir a derivação de classe
  2. As lambdas do C ++ 11 reduzem substancialmente a necessidade de classes de objeto de função nomeado (functor).
  3. Mova o construtor : as maneiras mágicas pelas quais os std::auto_ptrtrabalhos não são mais necessários devido ao suporte de primeira classe para referências de rvalor.
  4. Bool seguro : Isso foi mencionado anteriormente. Operadores explícitos do C ++ 11 evitam esse idioma C ++ 03 muito comum.
  5. Encolher para ajustar : Muitos contêineres STL C ++ 11 fornecem umashrink_to_fit() função de membro, que deve eliminar a necessidade de troca por um temporário.
  6. Classe base temporária : algumas bibliotecas C ++ antigas usam esse idioma bastante complexo. Com a semântica de movimentação, não é mais necessário.
  7. Enumeração segura do tipo Enumerações são muito seguras no C ++ 11.
  8. Proibindo a alocação de heap : A = deletesintaxe é uma maneira muito mais direta de dizer que uma funcionalidade específica é explicitamente negada. Isso é aplicável para impedir a alocação de heap (ou seja, =deletepara membro operator new), impedir cópias, atribuição, etc.
  9. Typedef com modelo : os modelos de alias no C ++ 11 reduzem a necessidade de typedef com modelo simples. No entanto, geradores de tipos complexos ainda precisam de meta-funções.
  10. Alguns cálculos numéricos de tempo de compilação, como Fibonacci, podem ser facilmente substituídos usando expressões constantes generalizadas
  11. result_of: Os usos do modelo de classe result_ofdevem ser substituídos por decltype. Eu acho que result_ofusa decltypequando está disponível.
  12. Os inicializadores de membros da classe salvam a digitação para a inicialização padrão de membros não estáticos com valores padrão.
  13. No novo código C ++ 11 NULLdeve ser redefinido como nullptr, mas veja a palestra da STL para saber por que eles decidiram contra.
  14. Os fanáticos do modelo de expressão têm o prazer de ter a sintaxe da função de tipo de retorno à direita no C ++ 11. Não há mais tipos de retorno longo de 30 linhas!

Eu acho que vou parar por aí!

Sumant
fonte
Obrigado pelo material detalhado!
Alan Baljeu
7
Ótima resposta, mas gostaria de encontrar a result_ofpartir da lista. Apesar do incômodo typenamenecessário antes, acho typename result_of<F(Args...)::typeque às vezes pode ser mais fácil de ler do que decltype(std::declval<F>()(std::declval<Args>()...), e com a aceitação do N3436 no documento de trabalho que ambos trabalham para a SFINAE (que costumava ser uma vantagem decltypedisso result_ofnão oferecia)
Jonathan Wakely
Em relação a 14) eu ainda estou chorando que eu tenho que usar macros, a fim de escrever o mesmo código duas vezes - uma vez para o corpo da função e uma vez para a declaração decltype () ...
2
Gostaria de observar que este tópico está vinculado nesta página da Microsoft como um artigo "Para mais informações" em uma introdução geral à linguagem C ++, mas este tópico é altamente especializado! Posso sugerir que um breve "Este tópico NÃO seja para iniciantes em C ++!" conselhos devem ser incluídos no início do tópico ou desta resposta?
Aacini
Re 12: "Inicialização de membro em classe" - esse é o novo idioma, não um idioma obsoleto, não é? Mudar a ordem das frases, talvez? Re 2: Functors são muito úteis quando você deseja passar tipos e não objetos (especialmente nos parâmetros do modelo). Portanto, apenas alguns usos de functores estão obsoletos.
einpoklum
66

Em um determinado momento, argumentou-se que se deveria retornar por constvalor, em vez de apenas por valor:

const A foo();
^^^^^

Isso foi principalmente inofensivo no C ++ 98/03 e pode até ter detectado alguns bugs parecidos com:

foo() = a;

Mas retornar por consté contra-indicado no C ++ 11, porque inibe a semântica de movimentos:

A a = foo();  // foo will copy into a instead of move into it

Então, basta relaxar e codificar:

A foo();  // return by non-const value
Howard Hinnant
fonte
9
Os erros evitáveis ​​agora podem ser detectados usando qualificadores de referência para funções. Como no caso acima, definindo em A& operator=(A o)&vez de A& operator=(A o). Isso evita erros tolos e faz com que as classes se comportem mais como tipos básicos e não impedem a semântica de movimentos.
Joe
61

Assim que você puder abandonar 0e a NULLfavor nullptr, faça-o!

No código não genérico, o uso 0ou NULLnão é tão importante. Mas assim que você começa a passar constantes nulos de ponteiro no código genérico, a situação muda rapidamente. Quando você passa 0para a template<class T> func(T) Té deduzido como uma intconstante de ponteiro e não como nula. E não pode ser convertido novamente em um ponteiro nulo constante depois disso. Isso se transforma em uma confusão de problemas que simplesmente não existem se o universo fosse usado apenas nullptr.

O C ++ 11 não é preterido 0e NULLcomo constantes de ponteiro nulo. Mas você deve codificar como se fosse.

Howard Hinnant
fonte
o que é decltype (nullptr)?
4
@GrapschKnutsch: É mesmo std::nullptr_t.
Howard Hinnant
Sugira que isso seja reformulado como o idioma reprovado, e não a nova convenção a ser adotada (por exemplo, "O uso de 0ou NULLpara ponteiros nulos").
einpoklum
38

Idioma seguro do boolexplicit operator bool().

Construtores de cópias privadas (boost :: noncopyable) → X(const X&) = delete

Simulando a classe final com destruidor privado e herança virtualclass X final

kennytm
fonte
exemplos bons e concisos, um dos quais ainda traz a palavra "idioma". bem colocado
Sebastian Mach
2
Uau, eu nunca vi o 'idioma seguro do bool' antes, parece bastante nojento! Espero nunca precisar dele no pré-C ++ 11 Código ...
boycy
24

Uma das coisas que apenas o impede de escrever algoritmos básicos no C ++ 11 é a disponibilidade de lambdas em combinação com os algoritmos fornecidos pela biblioteca padrão.

Estou usando esses agora e é incrível quantas vezes você apenas diz o que deseja fazer usando count_if (), for_each () ou outros algoritmos, em vez de precisar escrever os malditos loops novamente.

Depois de usar um compilador C ++ 11 com uma biblioteca padrão completa do C ++ 11, você não tem mais uma boa desculpa para não usar algoritmos padrão para criar os seus . Lambda apenas mate-o.

Por quê?

Na prática (depois de ter usado essa maneira de escrever algoritmos), é muito mais fácil ler algo que é construído com palavras diretas que significam o que é feito do que com alguns loops que você precisa criptografar para saber o significado. Dito isso, deduzir automaticamente os argumentos lambda ajudaria muito a tornar a sintaxe mais facilmente comparável a um loop bruto.

Basicamente, os algoritmos de leitura feitos com algoritmos padrão são muito mais fáceis, pois as palavras escondem os detalhes da implementação dos loops.

Suponho que apenas algoritmos de nível superior precisam ser pensados ​​agora que temos algoritmos de nível inferior para construir.

Klaim
fonte
8
Na verdade, há uma boa desculpa. Você está usando os algoritmos do Boost.Range , que são muito mais agradáveis;)
Nicol Bolas
10
Não vejo que for_eachcom um lambda seja melhor que o loop for baseado em intervalo equivalente, com o conteúdo do lambda no loop. O código parece mais ou menos o mesmo, mas o lambda introduz uma pontuação extra. Você pode usar equivalentes de coisas como boost::irangeaplicá-lo a mais loops do que apenas aqueles que obviamente usam iteradores. Além disso, o loop for baseado em intervalo tem maior flexibilidade, pois você pode sair mais cedo, se necessário (por returnou por break), enquanto que com for_eachvocê precisaria jogar.
21712 Steve Steveop
5
@SteveJessop: Mesmo assim, a disponibilidade do intervalo fortorna o it = c.begin(), const end = c.end(); it != end; ++itidioma usual extinto.
Ben Voigt
7
@SteveJessop Uma vantagem do for_eachalgoritmo no intervalo baseado no loop for é que você não pode break ou return. Ou seja, quando você vê for_eachque sabe imediatamente, sem olhar para o corpo, que não há tal truque.
bames53
5
@ Klaim: para ser específico, estou comparando, por exemplo, std::for_each(v.begin(), v.end(), [](int &i) { ++i; });com for (auto &i : v) { ++i; }. Aceito que a flexibilidade seja de dois gumes ( gotoé muito flexível, esse é o problema). Eu não acho que a restrição de não ser capaz de usar breaknos for_eachcompensa versão para a verbosidade extra que exige - usuários de for_eachaqui são IMO sacrificar a legibilidade real e conveniência para uma espécie de noção teórica que o for_eaché , em princípio, mais claro e conceitualmente mais simples. Na prática, não é mais claro ou mais simples.
21412 Steve Steveop
10

Você precisará implementar versões personalizadas com swapmenos frequência. No C ++ 03, swapmuitas vezes é necessário um não lançamento eficiente para evitar cópias caras e de lançamento, e como std::swapusa duas cópias, swapmuitas vezes precisa ser personalizado. No C ++, std::swapusa movee, portanto, o foco muda na implementação de construtores de movimento eficientes e não arremessadores e operadores de atribuição de movimento. Como para esses o padrão geralmente é bom, isso será muito menos trabalhoso do que no C ++ 03.

Geralmente, é difícil prever quais idiomas serão usados, pois são criados através da experiência. Podemos esperar um "C ++ 11 efetivo" talvez no próximo ano e um "Padrões de codificação C ++ 11" apenas em três anos, porque a experiência necessária ainda não existe.

Philipp
fonte
1
Eu sou duvidoso disso. O estilo recomendado é usar swap para mover e copiar a construção, mas não std :: swap, porque isso seria circular.
Alan Baljeu 17/02/2012
Sim, mas o construtor de movimentação normalmente chama uma troca personalizada ou é essencialmente equivalente.
Inverse
2

Não sei o nome, mas o código C ++ 03 costumava usar a seguinte construção como um substituto para a atribuição de movimentação ausente:

std::map<Big, Bigger> createBigMap(); // returns by value

void example ()
{
  std::map<Big, Bigger> map;

  // ... some code using map

  createBigMap().swap(map);  // cheap swap
}

Isso evitou qualquer cópia devido ao elision da cópia combinado com o swapacima.

Andrzej
fonte
1
No seu exemplo, a troca é desnecessária, a cópia elision construiria o valor de retorno de mapqualquer maneira. A técnica que você mostra é útil se mapjá existe, em vez de apenas estar sendo construída. O exemplo seria melhor sem o comentário "construtor padrão barato" e com "// ..." entre essa construção e a troca
Jonathan Wakely
Eu mudei de acordo com sua sugestão. Obrigado.
Andrzej
O uso de "grande" e "Maior" é confuso. Por que não explicar como os tamanhos da chave e o tipo de valor são importantes?
einpoklum
1

Quando notei que um compilador usando o padrão C ++ 11 não falha mais o seguinte código:

std::vector<std::vector<int>> a;

por supostamente conter o operador >>, comecei a dançar. Nas versões anteriores, seria preciso fazer

std::vector<std::vector<int> > a;

Para piorar as coisas, se você já teve que depurar isso, sabe como as mensagens de erro são horríveis.

Eu, no entanto, não sei se isso era "óbvio" para você.

v010dya
fonte
1
Esse recurso já foi adicionado no C ++ anterior. Ou pelo menos o Visual C ++ o implementou por discussão de padrões muitos anos antes.
Alan Baljeu
1
@AlanBaljeu Obviamente, existem muitas coisas não padronizadas sendo adicionadas às bibliotecas de compiladores. Havia muitos compiladores que tinham declaração de variável "automática" antes do C ++ 11, mas você não podia ter certeza de que seu código pode realmente ser compilado por qualquer outra coisa. A questão era sobre o padrão, não sobre "havia algum compilador que pudesse fazer isso".
precisa saber é o seguinte
1

Retorno por valor não é mais um problema. Com a semântica de movimentação e / ou as funções de codificação de otimização do valor de retorno (dependentes do compilador) são mais naturais, sem custos indiretos ou custos (na maioria das vezes).

Martin A
fonte
... mas qual idioma foi preterido?
einpoklum
Não é um idioma, mas não é mais uma boa prática. Mesmo com o RVO suportado pelo compilador, que é opcional. pt.wikipedia.org/wiki/Return_value_optimization "Nos estágios iniciais da evolução do C ++, a incapacidade da linguagem de retornar com eficiência um objeto do tipo classe de uma função era considerada uma fraqueza ..." "struct Data {char bytes [ 16]; }; void f (Dados * p) {// gera resultado diretamente em * p} int main () {Dados d; f (& d); }
Martin A
Eu estava sugerindo que você deveria formular sua resposta como "o costume de evitar o retorno por valor não é mais relevante, etc. etc. etc."
einpoklum