Uma vez fui entrevistado para o cargo de desenvolvedor de C ++ em um escritório decente e até bem conhecido. Eu já tinha alguma experiência na época, até fui chamado de desenvolvedor líder do meu empregador na época. Mas quando perguntado se eu sabia de coisas como DRY, KISS, YAGNI, NIH, tive que responder "Não" repetidamente.
Eu falhei miseravelmente, é claro. Mas as abreviações acima foram pesquisadas e lembradas. Ao ler artigos e livros temáticos, preparados para entrevistas e conversando com colegas, aprendi mais coisas novas, esqueci-as, pesquisei novamente no Google e entendi. Há alguns meses, um dos meus colegas mencionou casualmente no bate-papo de trabalho do IIFE no contexto de C ++. Como aquele avô em uma piada, quase caí do fogão e entrei novamente no Google.

Foi então que decidi compor (principalmente para mim) uma cábula para abreviações que são úteis para um desenvolvedor de C ++ saber. Isso não significa que eles se apliquem apenas ao C ++ ou que sejam conceitos todos do C ++ (você pode escrever volumes sobre idiomas da linguagem). Não, esses são apenas conceitos que eu realmente encontrei no trabalho e nas entrevistas, geralmente expressos na forma de abreviações. Bem, eu perdi coisas absolutamente triviais como LIFO, FIFO, CRUD, OOP, GCC e MSVC.
No entanto, as abreviaturas surgiram decentemente, então dividi a cábula em 2 partes: característica característica do C ++ e mais comum. Quando apropriado, agrupei os conceitos, caso contrário, simplesmente os listei em ordem alfabética. Em geral, não há muito sentido na ordem deles.
Coisas básicas:•
ODR•
POD•
POF•
PIMPLRAII•
RTTI•
STL•
UBSutilezas da língua:ADL•
CRTP•
CTAD•
EBO•
IIFE•
NVI•
RVO e NRVO•
SFINAE•
SBO, SOO, SSOATUALIZAÇÃO:•
CV•
LTO•
PCH•
PGO•
SEH / VEH•
TMP•
VLACoisas básicas
ODR
Regra de uma definição. A regra de uma definição. Simplificado significa o seguinte:
- Dentro de uma única unidade de tradução, cada variável, função, classe etc. não pode ter mais de uma definição. Existem tantos anúncios quanto possível (exceto transferências sem um determinado tipo de base, que simplesmente não podem ser declarados sem a definição), mas não mais de uma definição. Menos possível se a entidade não for usada.
- Durante todo o programa, cada função não linear e variável usada deve ter exatamente uma definição. Cada função embutida e variável usada deve ter uma definição em cada unidade de tradução.
- Algumas entidades - por exemplo, classes, funções embutidas e uma variável, modelos, enumerações etc. - podem ter várias definições em um programa (mas não mais que uma em uma unidade de tradução). Na verdade, isso acontece quando o mesmo cabeçalho que contém uma classe totalmente implementada, por exemplo, é conectado a vários arquivos .cpp. Mas essas definições devem coincidir (simplifico bastante, mas a essência é essa). Caso contrário, será UB .
O compilador detectará facilmente uma violação de
ODR dentro de uma unidade de tradução. Mas ele não poderá fazer nada se a regra for violada em uma escala de programa - apenas porque o compilador processa uma unidade de tradução por vez.
O vinculador pode encontrar muito mais violações, mas, estritamente falando, ele não é obrigado a fazer isso (porque, de acordo com o Padrão, a
UB está aqui) e ele pode perder alguma coisa. Além disso, o processo de busca de violações de
ODR no estágio de vinculação tem complexidade quadrática, e a montagem do código C ++ não é tão rápida.
Como resultado, a principal responsabilidade pelo cumprimento desta regra (especialmente no nível do programa) é o próprio desenvolvedor. E sim - apenas entidades com um link externo podem violar o
ODR em uma escala de programa; aqueles de dentro (ou seja, definidos em namespaces anônimos) não participam deste carnaval.
Leia mais: uma
vez (inglês) ,
duas (inglês)Pod
Dados antigos simples. Estrutura de dados simples. A definição mais simples: essa é uma estrutura que você pode, como está, em formato binário, enviar / receber da biblioteca C. Ou, o que é a mesma coisa, copie corretamente com
memcpy
simples.
De padrão para padrão, a definição completa foi alterada em detalhes. O
POD mais recente do C ++ 17 define atualmente como
- tipo escalar
- ou uma classe / estrutura / união que:
- existe uma classe trivial
- existe uma classe com um dispositivo padrão
- não contém campos não estáticos não- POD
- ou uma matriz desses tipos
Classe trivial:
- tem pelo menos um não excluído:
- construtor padrão
- construtor de cópias
- construtor em movimento
- operador de atribuição de cópias
- mover operador de atribuição
- todos os construtores padrão que copiam e movem construtores e operadores de atribuição são triviais (simplificados - gerados pelo compilador) ou remotos
- tem um destruidor não remoto trivial
- todos os tipos de base e todos os campos de tipos de classe têm destruidores triviais
- nenhum método virtual (incluindo destruidor)
- nenhum tipo de base virtual
Classe com um dispositivo padrão (classe de layout padrão):
No entanto, no C ++ 20 não haverá mais um conceito de tipo
POD , apenas o tipo trivial e o tipo com o dispositivo padrão permanecerão.
Leia mais:
um (russo) ,
dois (inglês) ,
três (inglês)POF
Função antiga simples. Uma função simples no estilo C. Mencionada no Padrão anterior ao C ++ 14, inclusive apenas no contexto de manipuladores de sinal. Os requisitos para isso são:
- usa apenas coisas comuns a C e C ++ (ou seja, sem exceções e
try-catch
, por exemplo) - não causa direta ou indiretamente funções não POF , com exceção de operações atômicas sem blocos (
std::atomic_init
, std::atomic_fetch_add
, etc.)
Somente essas funções, que também possuem um link C (
extern "C"
), são permitidas pela Norma como manipuladores de sinal. O suporte para outras funções depende do compilador.
No C ++ 17, o conceito de
POF desaparece, em vez de parecer uma avaliação segura do sinal. Em tais cálculos são proibidos:
- chama todas as funções da biblioteca padrão, exceto atômica, sem bloqueio
- chamadas
new
e delete
- usando
dynamic_cast
- chamar a entidade
thread_local
- qualquer trabalho com exceções
- inicialização de uma variável estática local
- aguardando a conclusão da inicialização da variável estática
Se o manipulador de sinal fizer alguma das opções acima, o Padrão promete
UB .
Leia mais:
time (inglês)PIMPL
Ponteiro para implementação. Ponteiro para implementação. O idioma clássico em C ++, também conhecido como ponteiro-d, ponteiro opaco, firewall de compilação. Consiste no fato de que todos os métodos privados, campos e outros detalhes de implementação de uma determinada classe são alocados em uma classe separada, e apenas métodos públicos (ou seja, uma interface) e um ponteiro para uma instância dessa nova classe separada permanecem na classe original. Por exemplo:
foo.hpp class Foo { public: Foo(); ~Foo(); void doThis(); int doThat(); private: class Impl; std::unique_ptr<Impl> pImpl_; };
foo.cpp #include "foo.hpp" class Foo::Impl {
Por que isso é necessário, ou seja, vantagens:
- Encapsulamento: os usuários da classe através da conexão do cabeçalho obtêm apenas o que precisam - uma interface pública. Se os detalhes da implementação mudarem, o código do cliente não precisará ser recompilado (consulte ABI ).
- Tempo de compilação: como o cabeçalho público não sabe nada sobre a implementação, ele não inclui os muitos cabeçalhos necessários. Consequentemente, o número de cabeçalhos implicitamente conectados no código do cliente é reduzido. A busca por nomes e a resolução de sobrecargas também é simplificada, porque o cabeçalho público não contém membros privados (embora sejam privados, eles participam desses processos).
Preço, ou seja, desvantagens:
Algumas dessas deficiências são removíveis, mas o preço está complicando ainda mais o código e introduzindo níveis adicionais de abstração (consulte
FTSE ).
Leia mais:
um (russo) ,
dois (russo) ,
três (inglês)RAII
Aquisição de recursos é inicialização. Capturar um recurso é inicialização. O significado desse idioma é que a retenção de um determinado recurso dura toda a vida útil do objeto correspondente. A captura do recurso ocorre no momento da criação / inicialização do objeto, a liberação - no momento da destruição / finalização do mesmo objeto.
Por incrível que pareça (principalmente para programadores de C ++), esse idioma também é usado em outras linguagens, mesmo naquelas com um coletor de lixo. Em Java, é
try--
, em Python a instrução
with
, em C # a diretiva
using
, em Go the
defer
. Mas é em C ++ com sua vida absolutamente previsível de objetos que o
RAII se encaixa especialmente organicamente.
Em C ++, um recurso geralmente é capturado no construtor e liberado no destruidor. Por exemplo, ponteiros inteligentes controlam a memória dessa maneira, fluxos de arquivos gerenciam arquivos, mutex bloqueia mutexes. O bom é que, não importa como o bloco sai (escopo) - é normal através de qualquer um dos pontos de saída ou foi lançada uma exceção - o objeto de controle de recurso criado nesse bloco será destruído e o recurso será liberado. I.e. Além de encapsular o
RAII no C ++, também ajuda a garantir a segurança no sentido de exceções.
Limitações, onde sem eles. Destruidores em C ++ não retornam valores e categoricamente não devem lançar exceções. Portanto, se a liberação do recurso for acompanhada por um ou outro, será necessário implementar lógica adicional no destruidor do objeto de controle.
Leia mais:
uma vez (russo) ,
dois (inglês)RTTI
Informações sobre o tipo de tempo de execução. Identificação do tipo em tempo de execução. Este é um mecanismo para obter informações sobre o tipo de um objeto ou expressão em tempo de execução. Existe em outros idiomas, mas em C ++ é usado para:
dynamic_cast
typeid
e type_info
- pegar exceção
Uma limitação importante: o
RTTI usa uma tabela de funções virtuais e, portanto, funciona apenas para tipos polimórficos (um destruidor virtual é suficiente). Uma explicação importante:
dynamic_cast
e
typeid
nem sempre usam
RTTI e, portanto, funcionam para tipos não polimórficos. Por exemplo, para converter dinamicamente um link para um descendente para um link para um ancestral, o
RTTI não
é necessário; todas as informações estão disponíveis no momento da compilação.
O RTTI não
é gratuito, embora um pouco, mas afeta negativamente o desempenho e o tamanho da memória consumida (daí o conselho frequente de não usar o
dynamic_cast
por causa de sua lentidão). Portanto, os compiladores, como regra, permitem desativar o
RTTI . O GCC e o MSVC prometem que isso não afetará a correção das exceções de captura.
Leia mais:
uma vez (russo) ,
dois (inglês)STL
Biblioteca de modelos padrão. Biblioteca de modelos padrão. Parte da biblioteca padrão C ++ que fornece contêineres genéricos, iteradores, algoritmos e funções auxiliares.
Apesar de seu nome bem conhecido,
STL nunca foi assim chamado no Padrão. Nas seções do Padrão, o
STL pode ser claramente atribuído à biblioteca Containers, Iterators, Algorithm Library e parcialmente à General utilities library.
Nas descrições de cargo, você pode encontrar 2 requisitos separados - conhecimento de C ++ e familiaridade com
STL . Eu nunca entendi isso, porque
STL é parte integrante da linguagem desde o primeiro padrão de 1998.
Leia mais:
uma vez (russo) ,
dois (inglês)UB
Comportamento indefinido. Comportamento indefinido. Esse comportamento ocorre nos casos de erro para os quais a Norma não possui requisitos. Muitos deles estão explicitamente listados no Padrão como levando ao
UB . Isso inclui, por exemplo:
- violação dos limites de uma matriz ou contêiner STL
- uso de variável não inicializada
- desreferenciando um ponteiro nulo
- estouro inteiro assinado
O resultado do
UB depende de tudo em uma linha - na versão do compilador e no clima em Marte. Além disso, esse resultado pode ser qualquer coisa: um erro de compilação, execução correta e falha. Comportamento indefinido é mau, é necessário se livrar dele.
O comportamento indefinido, por outro lado, não deve ser confundido com o
comportamento não especificado . Comportamento não especificado é o comportamento correto do programa correto, mas que, com a permissão do Padrão, depende do compilador. E o compilador não é necessário para documentá-lo. Por exemplo, esta é a ordem na qual os argumentos de uma função são avaliados ou os detalhes de implementação de
std::map
.
Bem, aqui você pode se lembrar do comportamento definido pela implementação. De não especificado difere na disponibilidade de documentação. Exemplo: o compilador é livre para criar o tipo
std::size_t
qualquer tamanho, mas deve indicar qual.
Leia mais:
um (russo) ,
dois (russo) ,
três (inglês)As sutilezas da língua
ADL
Pesquisa dependente de argumento. Pesquisa dependente de argumento. Ele é a busca por Koenig - em homenagem a Andrew Koenig. Este é um conjunto de regras para resolver nomes de funções não qualificados (ou seja, nomes sem o operador
::
:), além da resolução de nomes usual. Simplificando: o nome de uma função é pesquisado nos espaços de nomes relacionados aos seus argumentos (este é o espaço que contém o tipo do argumento, o próprio tipo, se for uma classe, todos os seus ancestrais etc.).
Exemplo mais simples #include <iostream> namespace N { struct S {}; void f(S) { std::cout << "f(S)" << std::endl; }; } int main() { N::S s; f(s); }
A função
f
encontrada no espaço para nome
N
apenas porque seu argumento pertence a esse espaço.
Até o trivial
std::cout << "Hello World!\n"
usa
ADL ,
std::basic_stream::operator<<
não
std::basic_stream::operator<<
sobrecarregado para
const char*
. Mas o primeiro argumento para esta declaração é
std::basic_stream
, e o compilador pesquisa e encontra uma sobrecarga adequada no
std
.
Alguns detalhes: A
ADL não
é aplicável se uma pesquisa regular encontrar uma declaração de um membro da classe ou uma declaração de função no bloco atual sem
using
, ou uma declaração de nem uma função nem um modelo de função. Ou se o nome da função estiver indicado entre parênteses (o exemplo acima não compila com
(f)(s)
; você precisará escrever
(N::f)(s);
).
Às vezes, a
ADL obriga a usar nomes de funções totalmente qualificados onde parece desnecessário.
Por exemplo, esse código não compila namespace N1 { struct S {}; void foo(S) {}; } namespace N2 { void foo(N1::S) {}; void bar(N1::S s) { foo(s); } }
Leia mais:
um (inglês) ,
dois (inglês) ,
três (inglês)CRTP
Curiosamente recorrente modelo padrão. Padrão recursivo estranho. A essência do modelo é a seguinte:
- alguma classe herda da classe de modelo
- a classe descendente é usada como um parâmetro de modelo de sua classe base
É mais fácil dar um exemplo:
template <class T> struct Base {}; struct Derived : Base<Derived> {};
O CRTP é um excelente exemplo de polimorfismo estático. A classe base fornece uma interface; as classes derivadas fornecem uma implementação. Mas, diferentemente do polimorfismo comum, não há sobrecarga para criar e usar uma tabela de funções virtuais.
Exemplo template <typename T> struct Base { void action() const { static_cast<T*>(this)->actionImpl(); } }; struct Derived : Base<Derived> { void actionImpl() const { ... } }; template <class Arg> void staticPolymorphicHandler(const Arg& arg) { arg.action(); }
Quando usado corretamente,
T
sempre um descendente de
Base
, portanto,
static_cast
é suficiente para
static_cast
. Sim, neste caso, a classe base conhece a interface descendente.
Outra área comum de uso do
CRTP é a extensão (ou restrição) da funcionalidade das classes herdadas (algo chamado mixin em alguns idiomas). Talvez os exemplos mais famosos:
struct Derived : singleton<Derived> { … }
struct Derived : private boost::noncopyable<Derived> { … }
struct Derived : std::enable_shared_from_this<Derived> { … }
struct Derived : counter<Derived> { … }
- conta o número de objetos criados e / ou existentes
Desvantagens, ou melhor, momentos que requerem atenção:
- Não há classe base comum, você não pode criar uma coleção de descendentes diferentes e acessá-los através de um ponteiro para o tipo base. Mas se você quiser, poderá herdar o Base do tipo polimórfico usual.
- Há uma oportunidade adicional de tirar o pé do descuido:
Exemplo template <typename T> struct Base {}; struct Derived1 : Base<Derived1> {}; struct Derived2 : Base<Derived1> {};
Mas você pode adicionar proteção:
private: Base() = default; friend T;
- Porque Como todos os métodos são não virtuais, os métodos do descendente ocultam os métodos da classe base com os mesmos nomes. Portanto, é melhor chamá-los de maneira diferente.
- Em geral, os descendentes têm métodos públicos que não devem ser usados em nenhum lugar, exceto na classe base. Isso não é bom, mas é corrigido através de um nível adicional de abstração (consulte FTSE ).
Leia mais:
uma vez (russo) ,
dois (inglês)CTAD
Dedução de Argumento do Modelo de Classe. Inferindo automaticamente o tipo do parâmetro do modelo de classe. Este é um novo recurso do C ++ 17. Anteriormente, apenas os tipos de variáveis (
auto
) e os parâmetros do modelo de função eram exibidos automaticamente, e é por isso que funções auxiliares como
std::make_pair
,
std::make_tuple
etc.
std::make_tuple
, na maioria das vezes, elas não são necessárias, porque o compilador capaz de exibir automaticamente os parâmetros dos modelos de classe:
std::pair p{1, 2.0};
O CTAD é uma nova oportunidade,
precisa ser mais desenvolvido e desenvolvido (o C ++ 20 já promete melhorias). Enquanto isso, as restrições são as seguintes:
Inferência parcial de tipos de parâmetro não é suportada std::pair<double> p{1, 2};
Aliases de modelo não suportados template <class T, class U> using MyPair = std::pair<T, U>; MyPair p{1, 2};
Os construtores disponíveis apenas nas especializações de modelo não são suportados. template <class T> struct Wrapper {}; template <> struct Wrapper<int> { Wrapper(int) {}; }; Wrapper w{5};
Modelos aninhados não são suportados template <class T> struct Foo { template <class U> struct Bar { Bar(T, U) {}; }; }; Foo::Bar x{ 1, 2.0 };
Obviamente, o CTAD não funcionará se o tipo do parâmetro do modelo não estiver relacionado aos argumentos do construtor. template <class T> struct Collection { Collection(std::size_t size) {}; }; Collection c{5};
Em alguns casos, regras explícitas de inferência que devem ser declaradas no mesmo bloco que o modelo de classe ajudarão.
Exemplo template <class T> struct Collection { template <class It> Collection(It from, It to) {}; }; Collection c{v.begin(), v.end()};
Leia mais:
uma vez (russo) ,
dois (inglês)EBO
Otimização da base vazia. Otimização de uma classe base vazia. Também chamado de EBCO (Empty Base Class Optimization).
Como você sabe, em C ++, o tamanho de um objeto de qualquer classe não pode ser zero. Caso contrário, toda a aritmética dos ponteiros será interrompida, porque em um endereço será possível marcar quantos objetos diferentes você desejar. Portanto, mesmo objetos de classes vazias (ou seja, classes sem um único campo não estático) têm um tamanho diferente de zero, que depende do compilador e do SO e geralmente é igual a 1.
Assim, a memória é desperdiçada em vão em todos os objetos de classes vazias. Mas não os objetos de seus descendentes, porque neste caso o Padrão explicitamente faz uma exceção. É permitido ao compilador não alocar memória para uma classe base vazia e, assim, salvar não apenas 1 byte da classe vazia, mas todos os 4 (dependendo da plataforma), pois também há alinhamento.
Exemplo struct Empty {}; struct Foo : Empty { int i; }; std::cout << sizeof(Empty) << std::endl;
Mas
como objetos diferentes do mesmo tipo não podem ser colocados no mesmo endereço, o
EBO não funcionará se:
Uma classe vazia é encontrada duas vezes entre ancestrais struct Empty {}; struct Empty2 : Empty {}; struct Foo : Empty, Empty2 { int i; }; std::cout << sizeof(Empty) << std::endl;
O primeiro campo não estático é um objeto da mesma classe vazia ou de seu descendente struct Empty {}; struct Foo : Empty { Empty e; int i; }; std::cout << sizeof(Empty) << std::endl;
Nos casos em que objetos de classes vazias são campos não estáticos, nenhuma otimização é fornecida (por enquanto, o atributo
[[no_unique_address]]
aparecerá em C ++ 20). Mas gastar 4 bytes (ou quanto o compilador precisa) para cada um desses campos é uma pena, então você pode "recolher" objetos de classes vazias com o primeiro campo não estático não vazio por conta própria.
Exemplo struct Empty1 {}; struct Empty2 {}; template <class Member, class ... Empty> struct EmptyOptimization : Empty ... { Member member; }; struct Foo { EmptyOptimization<int, Empty1, Empty2> data; };
Estranho, mas nesse caso, o tamanho do Foo é diferente para diferentes compiladores, para o MSVC 2019 é 8, para o GCC 8.3.0 é 4. Mas, em qualquer caso, aumentar o número de classes vazias não afeta o tamanho do
Foo
.
Leia mais: uma
vez (inglês) ,
duas (inglês)IIFE
Expressão de função invocada imediatamente. Expressão funcional chamada imediatamente. Em geral, esse é um idioma em JavaScript, de onde Jason Turner o emprestou junto com o nome. Na verdade, é apenas criar e chamar imediatamente um lambda:
const auto myVar = [&] { if (condition1()) { return computeSomeComplexStuff(); } return condition2() ? computeSonethingElse() : DEFAULT_VALUE; } ();
Por que isso é necessário? Bem, por exemplo, como no código acima, para inicializar uma constante pelo resultado de um cálculo não trivial e não entupir o escopo com variáveis e funções desnecessárias.
Leia mais: uma
vez (inglês) ,
duas (inglês)NVI
Interface não virtual. Interface não virtual. De acordo com esse idioma, uma interface de classe aberta não deve conter funções virtuais. Todas as funções virtuais são tornadas privadas (máximo protegido) e chamadas dentro de não virtuais abertas.
Exemplo class Base { public: virtual ~Base() = default; void foo() {
Por que isso é necessário:
- Cada função virtual aberta faz duas coisas: define a interface pública da classe e participa da redefinição de comportamento nas classes descendentes. O uso de NVI elimina essas funções com uma carga dupla: a interface é definida por algumas funções, mudança de comportamento por outras. Você pode alterar os dois independentemente um do outro.
- (- -, . .), (. DRY ) — — . I.e. .
NVI – , (- ) (.
FBC ).
:
(.) ,
(.)RVO NRVO
(Named) Return Value Optimization. () . copy elision – , . , ( copy elision – ).
Foo bar() { return Foo(); } int main() { auto f = bar(); }
RVO Foo
bar
,
main
(
bar
),
f
.
RVO ,
bar
f
.
:
main
f
.
bar
( ), , .
NRVO RVO , ,
return
, .
Foo bar() { Foo result; return result; }
,
NRVO , . , , , —
NRVO .
NRVO Foo bar(bool condition) { if (condition) { Foo f1; return f1; } Foo f2; return f2; }
RVO .
NRVO .
RVO NRVO – . , . C++17:
RVO copy elision, , .
:
(N)RVO — . C++14 , C++17
RVO , C++20 – .
. -,
(N)RVO - , .. . -,
result
std::move(result)
,
NRVO . :
RVO prvalue,
NRVO – lvalue, a
std::move(result)
– xvalue.
:
(.) ,
(.) ,
(.)SFINAE
Substitution Failure Is Not An Error. — . SFINAE — — — ++. , , , . , :
- — (. ADL ).
- — , , , . .
- (viable functions), . — .
SFINAE : , , , ( ). .
SFINAE , , . - , . . , .
#include <iostream> #include <type_traits> #include <utility> template <class, class = void> struct HasToString : std::false_type {}; // , // - , // — , , template <class T> struct HasToString<T, std::void_t<decltype(&T::toString)>> : std::is_same<std::string, decltype(std::declval<T>().toString())> {}; struct Foo { std::string toString() { return {}; } }; int main() { std::cout << HasToString<Foo>::value << std::endl; // 1 std::cout << HasToString<int>::value << std::endl; // 0 }
C++17
static if
SFINAE , C++20 . .
:
(.) ,
(.) ,
(.)SBO, SOO, SSO
Small Buffer/Object/String Optimization. //. SSO Small Size Optimization, , ,
SSO – .
SBO SOO – ,
SSO – .
, , - . , . , ( ), .
, std::string :
class string { char* begin_; size_t size_; size_t capacity_; };
24 ( ). I.e. 24 . 24, , . . - . 8 ( — 24 ):
class string { union Buffer { char* begin_; char local_[8]; }; Buffer buffer_; size_t _size; size_t _capacity; };
, — . .
std::string
SSO std::function
.
std::vector
, . . ,
std::swap
, .
SBO (
std::string
).
boost::container::small_vector
, ,
SBO .
:
(.) ,
(.)UDPATE
PyerK .
CV
const volatile. const
, / , ,
UB .
volatile
, / (, - - ), .
volatile
volatile
UB .
:
(.) ,
(.) ,
(.)LTO
Link Time Optimization. . , , . . . , : , . , .
:
(.)PCH
Precompiled Headers. . , . , .
:
(.)PGO
Profile-Guided Optimization. . , , . , .
:
(.)SEH/VEH
Structured/Vectored Exception Handling. MSVC .
try-catch
SEH :
__try
,
__except
,
__finally
, , , , - , . .
VEH , .
:
(.)TMP
Template Meta-Programming. . — . C++ . . ,
TMP C++ , . . .
:
(.)VLA
Variable-Length Arrays. . I.e. , :
void foo(int n) { int array[n]; }
C++ . , . . C C99. C++ .
:
(.)PS
- - — . , , , C++. , , .