Referência rápida das categorias de valor C ++: Parte 1

imagem

O objetivo dessa referência rápida é coletar em um único local e organizar informações sobre categorias de valor em C ++, atribuição, passagem de parâmetro e retorno de funções. Tentei tornar essa referência rápida conveniente para comparar e selecionar rapidamente uma das soluções possíveis, por isso fiz várias tabelas aqui.


Para introdução ao tópico, use os seguintes links:


C ++ rvalue referências e mover semântica para iniciantes
Valores redefinidos
C ++ move-se para pessoas que não sabem ou se importam com o que são os valores
Scott Meyers. C ++ moderno eficaz. 2015
Noções básicas sobre a semântica de movimentos e o encaminhamento perfeito: parte 1
Noções básicas sobre a semântica de movimentos e o encaminhamento perfeito: parte 2
Noções básicas sobre a semântica de movimentos e o encaminhamento perfeito: parte 3
Precisamos mover e copiar a atribuição


Categorias de valor


imagem


A partir do C ++ 11, qualquer expressão pode pertencer apenas a uma das três categorias de valor: lvalue, xvalue ou prvalue:


  • xvalue (valor expirado) - expressão, que identifica um objeto não temporário, que pode ser movido
  • lvalue (valor à esquerda) - expressão, que identifica um objeto não temporário da função, que não pode ser movido
  • prvalue (valor puro) - expressão, que inicializa um objeto

imagem


Essas três categorias podem ser agrupadas em três grupos sobrepostos:


  • glvalue (lvalue generalizado) agrupa lvalue e xvalue. glvalue tem endereço na memória (tem identidade) e, portanto, geralmente pode ser atribuído um valor (se não for const)
  • rvalue agrupa prvalue e xvalue. O rvalue pode ser movido (xvalue) ou não pertence a nenhum objeto existente (prvalue). rvalue pode ser passado para mover construtores, operadores de atribuição ou funções

Portanto, xvalue tem um endereço na memória e pode ser movido.


imagem


Você pode ver na tabela acima que as variáveis ​​nomeadas das categorias lvalue, lvalue reference e rvalue reference têm todas a mesma categoria de expressão: lvalue (destacado em vermelho). Por exemplo, isso significa que quando a referência rvalue é passada para uma função, uma sobrecarga de referência lvalue será escolhida: T&& x=T(); f(x); T&& x=T(); f(x);


Ligações:
C ++ lvalue rvalue xvalor glvalue prvalue
Categorias de valor em C ++ 17
Categorias de valor


Abreviações neste artigo


Abreviações de construtores, operadores e destruidores:


  • Dc - Construtor Padrão
  • Pc - Construtor parametrizado
  • Cc - Construtor de cópia
  • Ca - Cópia de atribuição (pode reutilizar o armazenamento alocado, por exemplo, para std :: string)
  • Construtor Mc - Move
  • Ma - Mover atribuição
  • Va - Operador de atribuição de conversão
  • D - Destruidor

Abreviações de categorias de valor:


  • LV - Valor: T LV;
  • LR - referência de valor: T& LR = LV;
  • XV - Xvalue: move(LV)
  • PRV - valor puro. Literal ou resultado de chamada de função, cujo tipo de retorno não é uma referência: T f() { return T(); } T f() { return T(); }
  • FR - Referência de encaminhamento: auto&& FR = LV;
  • CLV - const LV. CLR - const LR. CXV - const XV.
  • CPRV - PRV const. O CPRV existe apenas para classes e matrizes. CPRV sem classe e sem classe, como cont int , será implicitamente convertido em PRV como int , por exemplo, no valor de retorno da função const int f() )
  • RV é XV ou PRV
  • CRV é CXV ou CPRV

Outras abreviações:


  • PF - Forwarding perfeito (usando referência de encaminhamento modelado para passar o parâmetro para a função)

Atribuindo categorias de valor


A atribuição de categorias de valor pode ser vista como a base de todas as seções a seguir. Ao atribuir explicitamente uma categoria a outra sem conversão explícita de categoria, ocorre uma conversão implícita: FROM_TYPE x; TO_TYPE y; y = x; FROM_TYPE x; TO_TYPE y; y = x; Na tabela abaixo, você pode ver o que acontece quando a expressão do tipo From é atribuída a uma variável do tipo To:


  • Um sinal de menos significa que tal tarefa não é possível
  • Um sinal de adição significa que essa atribuição é possível e não exigirá a chamada de operadores / construtores de copiar / mover.
  • Em outros casos, há texto, o que significa quais operadores de cópia / movimentação e construtores serão chamados durante essa atribuição.

As conversões, que não podem ocorrer implicitamente da categoria de expressão retornada para o tipo de valor de retorno da função, são marcadas com fonte vermelha - consulte a seção "Retornando de uma função" abaixo para obter detalhes.
Linhas com tipos constantes são destacadas em azul.


imagem


Notas de rodapé:


1 - Const auto && references não são referências de encaminhamento, mas const rvalue reference, portanto, elas atuarão como const T &&
2 - um temporário criado para manter um inicializador de referência persiste até o final do escopo de sua referência (consulte a próxima seção). Não funciona para converter o valor de retorno no tipo de função.
3 - ao passar literal, precisa do especificador de tipo à direita para auto: auto&& = T{2};


Nota:


  • As referências não const lvalue e rvalue não podem aceitar nenhum tipo const. Mas o tipo não-const e não-referência pode aceitar tipos const.
  • A referência de valor (T&&) pode aceitar xvalue ou prvalue (XV ou PRV).
  • A referência lvalue (T &) pode aceitar lvalue ou lvalue reference (LV ou LR)
  • Os tipos que não são de referência aceitam qualquer tipo convertendo, copiando, movendo ou RVO
  • As referências de valor const (const T &, const auto &) e referências de encaminhamento automático (auto &&) aceitam qualquer tipo sem copiar ou mover
  • As referências const e não-const rvalue (T &&, const T &&) não podem aceitar valores const ou não-const (LV ou CLV).
  • O LR não pode aceitar referências de valores constantes ou não constantes (XV ou PRV ou CXV ou CPRV). O CLR pode aceitá-los.
  • Tipos com modificadores const não podem ser atribuídos a nenhum tipo sem modificadores const (exceto auto, que pode se tornar const se const estiver explícito no lado direito).
  • A passagem de prvalue (passagem de valor de retorno, resultado literal ou construtor) é a mesma que passagem de xvalue, mas evita chamar o construtor de movimentação ao atribuir ao tipo não de referência
  • Passar o valor pr const (passar o valor de retorno da função tipo const) é o mesmo que passar const xvalue, mas evita chamar o construtor de cópia ao atribuir ao tipo não de referência
  • As referências automáticas podem se tornar const, para que possam aceitar variantes const e não-const. Além disso, eles podem se tornar referências const para tornar a persistência temporária até o final do escopo da referência. Mas as referências automáticas * não podem se tornar const se isso não for explicitamente especificado no lado direito. É por isso que você não pode atribuir rvalue a auto &
  • A referência const pode receber um temporário e persistir até o final do escopo de referência (a referência não const não pode). Isso funciona apenas dentro do escopo atual (referências de função local e argumentos de função). Se a referência for um membro de uma classe, o objeto temporário será destruído no final do construtor ou função e a referência continuará apontando para um objeto destruído, que é um comportamento indefinido.
  • Auto x = CLV; deduzirá automático para o tipo não const.

Exemplos e testes com impressão de cada construtor e operador chamado
 #include <iostream> #include <iomanip> #include <map> #include <vector> #include <string> using namespace std; template<class C, class T> auto contains(const C& v, const T& x) -> decltype(end(v), true) { return end(v) != std::find(begin(v), end(v), x); } template <class... Types> constexpr inline __attribute__((__always_inline__)) int UNUSED(Types&&...) { return 0; }; map<string, map<string, string>> res; vector<string> froms; vector<string> tos; string from; string to; void report(string st) { if (!from.empty() && !to.empty()) { res[from][to] += st; } cout << st << " "; } struct T { T() { report("Dc"); } T(int va) : a(va) { report("Pc"); } T(const T& other) : a(other.a) { report("Cc"); } T(T&& other) : a(std::exchange(other.a, 0)) { report("Mc"); } T& operator=(int va) { report("Va"); a = va; return *this; } T& operator=(const T& rhs) { report("Ca"); // check for self-assignment if(&rhs == this) return *this; a = rhs.a; return *this; } T& operator=(T&& rhs) { report("Ma"); // check for self-assignment if(&rhs == this) return *this; a = std::exchange(rhs.a, 0); return *this; } ~T() { report("D"); } int a = 1; }; T Fprv() { return T(); } const T Fcprv() { return T(); } void print_col(const string &st, int width) { cout << endl << left << setw(width) << st; } void test_assign(string lto, string lfrom) { from = lfrom; to = lto; res[from][to] = ""; if (!from.empty() && !to.empty()) { if (!contains(froms, from)) froms.push_back(from); if (!contains(tos, to)) tos.push_back(to); } print_col(lto + " = " + lfrom + ": ", 20); } #define TEST_ASSIGN(t, v) { \ test_assign(#t, #v); \ ts = v; \ cout << sa; \ UNUSED(s); \ cout << "-"; \ } void test_conversion() { T l; const T cl; T& lr = l; const T& clr = l; T&& rr = T(); const T&& crr = T(); auto &&fr = T(); TEST_ASSIGN(T, 8); TEST_ASSIGN(T, T()); TEST_ASSIGN(T, l); TEST_ASSIGN(T, move(l)); TEST_ASSIGN(T, cl); TEST_ASSIGN(T, move(cl)); TEST_ASSIGN(T, lr); TEST_ASSIGN(T, move(lr)); TEST_ASSIGN(T, clr); TEST_ASSIGN(T, move(clr)); TEST_ASSIGN(T, rr); TEST_ASSIGN(T, move(rr)); TEST_ASSIGN(T, crr); TEST_ASSIGN(T, move(crr)); TEST_ASSIGN(T, Fcprv()); TEST_ASSIGN(T, Fprv()); TEST_ASSIGN(T, fr); TEST_ASSIGN(T, move(fr)); TEST_ASSIGN(const T, 8); TEST_ASSIGN(const T, T()); TEST_ASSIGN(const T, l); TEST_ASSIGN(const T, move(l)); TEST_ASSIGN(const T, cl); TEST_ASSIGN(const T, move(cl)); TEST_ASSIGN(const T, lr); TEST_ASSIGN(const T, move(lr)); TEST_ASSIGN(const T, clr); TEST_ASSIGN(const T, move(clr)); TEST_ASSIGN(const T, rr); TEST_ASSIGN(const T, move(rr)); TEST_ASSIGN(const T, crr); TEST_ASSIGN(const T, move(crr)); TEST_ASSIGN(const T, Fcprv()); TEST_ASSIGN(const T, Fprv()); TEST_ASSIGN(const T, fr); TEST_ASSIGN(const T, move(fr)); //TEST_ASSIGN(T&, 8); //TEST_ASSIGN(T&, T()); TEST_ASSIGN(T&, l); //TEST_ASSIGN(T&, move(l)); //TEST_ASSIGN(T&, cl); //TEST_ASSIGN(T&, move(cl)); TEST_ASSIGN(T&, lr); //TEST_ASSIGN(T&, move(lr)); //TEST_ASSIGN(T&, clr); //TEST_ASSIGN(T&, move(clr)); //TEST_ASSIGN(T&, rr); //TEST_ASSIGN(T&, move(rr)); //TEST_ASSIGN(T&, crr); //TEST_ASSIGN(T&, move(crr)); //TEST_ASSIGN(T&, Fcprv()); //TEST_ASSIGN(T&, Fprv()); TEST_ASSIGN(T&, fr); //TEST_ASSIGN(T&, move(fr)); TEST_ASSIGN(const T&, 8); TEST_ASSIGN(const T&, T()); TEST_ASSIGN(const T&, l); TEST_ASSIGN(const T&, move(l)); TEST_ASSIGN(const T&, cl); TEST_ASSIGN(const T&, move(cl)); TEST_ASSIGN(const T&, lr); TEST_ASSIGN(const T&, move(lr)); TEST_ASSIGN(const T&, clr); TEST_ASSIGN(const T&, move(clr)); TEST_ASSIGN(const T&, rr); TEST_ASSIGN(const T&, move(rr)); TEST_ASSIGN(const T&, crr); TEST_ASSIGN(const T&, move(crr)); TEST_ASSIGN(const T&, Fcprv()); TEST_ASSIGN(const T&, Fprv()); TEST_ASSIGN(const T&, fr); TEST_ASSIGN(const T&, move(fr)); TEST_ASSIGN(T&&, 8); TEST_ASSIGN(T&&, T()); //TEST_ASSIGN(T&&, l); TEST_ASSIGN(T&&, move(l)); //TEST_ASSIGN(T&&, cl); //TEST_ASSIGN(T&&, move(cl)); //TEST_ASSIGN(T&&, lr); TEST_ASSIGN(T&&, move(lr)); //TEST_ASSIGN(T&&, clr); //TEST_ASSIGN(T&&, move(clr)); //TEST_ASSIGN(T&&, rr); TEST_ASSIGN(T&&, move(rr)); //TEST_ASSIGN(T&&, crr); //TEST_ASSIGN(T&&, move(crr)); //TEST_ASSIGN(T&&, Fcprv()); TEST_ASSIGN(T&&, Fprv()); //TEST_ASSIGN(T&&, fr); TEST_ASSIGN(T&&, move(fr)); TEST_ASSIGN(const T&&, 8); TEST_ASSIGN(const T&&, T()); //TEST_ASSIGN(const T&&, l); TEST_ASSIGN(const T&&, move(l)); //TEST_ASSIGN(const T&&, cl); TEST_ASSIGN(const T&&, move(cl)); //TEST_ASSIGN(const T&&, lr); TEST_ASSIGN(const T&&, move(lr)); //TEST_ASSIGN(const T&&, clr); TEST_ASSIGN(const T&&, move(clr)); //TEST_ASSIGN(const T&&, rr); TEST_ASSIGN(const T&&, move(rr)); //TEST_ASSIGN(const T&&, crr); TEST_ASSIGN(const T&&, move(crr)); TEST_ASSIGN(const T&&, Fcprv()); TEST_ASSIGN(const T&&, Fprv()); //TEST_ASSIGN(const T&&, fr); TEST_ASSIGN(const T&&, move(fr)); //TEST_ASSIGN(auto&, T{8}); //TEST_ASSIGN(auto&, T()); TEST_ASSIGN(auto&, l); //TEST_ASSIGN(auto&, move(l)); TEST_ASSIGN(auto&, cl); TEST_ASSIGN(auto&, move(cl)); TEST_ASSIGN(auto&, lr); //TEST_ASSIGN(auto&, move(lr)); TEST_ASSIGN(auto&, clr); TEST_ASSIGN(auto&, move(clr)); TEST_ASSIGN(auto&, rr); //TEST_ASSIGN(auto&, move(rr)); TEST_ASSIGN(auto&, crr); TEST_ASSIGN(auto&, move(crr)); TEST_ASSIGN(auto&, Fcprv()); //TEST_ASSIGN(auto&, Fprv()); TEST_ASSIGN(auto&, fr); //TEST_ASSIGN(auto&, move(fr)); TEST_ASSIGN(const auto&, T{8}); TEST_ASSIGN(const auto&, T()); TEST_ASSIGN(const auto&, l); TEST_ASSIGN(const auto&, move(l)); TEST_ASSIGN(const auto&, cl); TEST_ASSIGN(const auto&, move(cl)); TEST_ASSIGN(const auto&, lr); TEST_ASSIGN(const auto&, move(lr)); TEST_ASSIGN(const auto&, clr); TEST_ASSIGN(const auto&, move(clr)); TEST_ASSIGN(const auto&, rr); TEST_ASSIGN(const auto&, move(rr)); TEST_ASSIGN(const auto&, crr); TEST_ASSIGN(const auto&, move(crr)); TEST_ASSIGN(const auto&, Fcprv()); TEST_ASSIGN(const auto&, Fprv()); TEST_ASSIGN(const auto&, fr); TEST_ASSIGN(const auto&, move(fr)); TEST_ASSIGN(auto&&, T{8}); TEST_ASSIGN(auto&&, T()); TEST_ASSIGN(auto&&, l); TEST_ASSIGN(auto&&, move(l)); TEST_ASSIGN(auto&&, cl); TEST_ASSIGN(auto&&, move(cl)); TEST_ASSIGN(auto&&, lr); TEST_ASSIGN(auto&&, move(lr)); TEST_ASSIGN(auto&&, clr); TEST_ASSIGN(auto&&, move(clr)); TEST_ASSIGN(auto&&, rr); TEST_ASSIGN(auto&&, move(rr)); TEST_ASSIGN(auto&&, crr); TEST_ASSIGN(auto&&, move(crr)); TEST_ASSIGN(auto&&, Fcprv()); TEST_ASSIGN(auto&&, Fprv()); TEST_ASSIGN(auto&&, fr); TEST_ASSIGN(auto&&, move(fr)); TEST_ASSIGN(const auto&&, T{8}); TEST_ASSIGN(const auto&&, T()); //TEST_ASSIGN(const auto&&, l); TEST_ASSIGN(const auto&&, move(l)); //TEST_ASSIGN(const auto&&, cl); TEST_ASSIGN(const auto&&, move(cl)); //TEST_ASSIGN(const auto&&, lr); TEST_ASSIGN(const auto&&, move(lr)); //TEST_ASSIGN(const auto&&, clr); TEST_ASSIGN(const auto&&, move(clr)); //TEST_ASSIGN(const auto&&, rr); TEST_ASSIGN(const auto&&, move(rr)); //TEST_ASSIGN(const auto&&, crr); TEST_ASSIGN(const auto&&, move(crr)); TEST_ASSIGN(const auto&&, Fcprv()); TEST_ASSIGN(const auto&&, Fprv()); //TEST_ASSIGN(const auto&&, fr); TEST_ASSIGN(const auto&&, move(fr)); cout << endl; const int twidth = 9; cout << left << setw(twidth) << "From:"; for (const auto& lto : tos) { cout << left << setw(twidth) << lto; } cout << endl; for (const auto& lfrom : froms) { cout << left << setw(twidth) << lfrom; for (const auto& lto : tos) { if (!res.count(lfrom) || !res[lfrom].count(lto)) { cout << left << setw(twidth) << "-"; } else if (res[lfrom][lto].empty()) { cout << left << setw(twidth) << "+"; } else { cout << left << setw(twidth) << res[lfrom][lto]; } } cout << endl; } cout << endl; } int main() { test_conversion(); cout << endl; return 0; } /* Output: Dc Dc Dc Dc Dc T = 8: Pc 8-D T = T(): Dc 1-D T = l: Cc 1-D T = move(l): Mc 1-D T = cl: Cc 1-D T = move(cl): Cc 1-D T = lr: Cc 0-D T = move(lr): Mc 0-D T = clr: Cc 0-D T = move(clr): Cc 0-D T = rr: Cc 1-D T = move(rr): Mc 1-D T = crr: Cc 1-D T = move(crr): Cc 1-D T = Fcprv(): Dc 1-D T = Fprv(): Dc 1-D T = fr: Cc 1-D T = move(fr): Mc 1-D const T = 8: Pc 8-D const T = T(): Dc 1-D const T = l: Cc 0-D const T = move(l): Mc 0-D const T = cl: Cc 1-D const T = move(cl): Cc 1-D const T = lr: Cc 0-D const T = move(lr): Mc 0-D const T = clr: Cc 0-D const T = move(clr): Cc 0-D const T = rr: Cc 0-D const T = move(rr): Mc 0-D const T = crr: Cc 1-D const T = move(crr): Cc 1-D const T = Fcprv(): Dc 1-D const T = Fprv(): Dc 1-D const T = fr: Cc 0-D const T = move(fr): Mc 0-D T& = l: 0- T& = lr: 0- T& = fr: 0- const T& = 8: Pc 8-D const T& = T(): Dc 1-D const T& = l: 0- const T& = move(l): 0- const T& = cl: 1- const T& = move(cl): 1- const T& = lr: 0- const T& = move(lr): 0- const T& = clr: 0- const T& = move(clr): 0- const T& = rr: 0- const T& = move(rr): 0- const T& = crr: 1- const T& = move(crr): 1- const T& = Fcprv(): Dc 1-D const T& = Fprv(): Dc 1-D const T& = fr: 0- const T& = move(fr): 0- T&& = 8: Pc 8-D T&& = T(): Dc 1-D T&& = move(l): 0- T&& = move(lr): 0- T&& = move(rr): 0- T&& = Fprv(): Dc 1-D T&& = move(fr): 0- const T&& = 8: Pc 8-D const T&& = T(): Dc 1-D const T&& = move(l): 0- const T&& = move(cl): 1- const T&& = move(lr): 0- const T&& = move(clr): 0- const T&& = move(rr): 0- const T&& = move(crr): 1- const T&& = Fcprv(): Dc 1-D const T&& = Fprv(): Dc 1-D const T&& = move(fr): 0- auto& = l: 0- auto& = cl: 1- auto& = move(cl): 1- auto& = lr: 0- auto& = clr: 0- auto& = move(clr): 0- auto& = rr: 0- auto& = crr: 1- auto& = move(crr): 1- auto& = Fcprv(): Dc 1-D auto& = fr: 0- const auto& = T{8}: Pc 8-D const auto& = T(): Dc 1-D const auto& = l: 0- const auto& = move(l): 0- const auto& = cl: 1- const auto& = move(cl): 1- const auto& = lr: 0- const auto& = move(lr): 0- const auto& = clr: 0- const auto& = move(clr): 0- const auto& = rr: 0- const auto& = move(rr): 0- const auto& = crr: 1- const auto& = move(crr): 1- const auto& = Fcprv(): Dc 1-D const auto& = Fprv(): Dc 1-D const auto& = fr: 0- const auto& = move(fr): 0- auto&& = T{8}: Pc 8-D auto&& = T(): Dc 1-D auto&& = l: 0- auto&& = move(l): 0- auto&& = cl: 1- auto&& = move(cl): 1- auto&& = lr: 0- auto&& = move(lr): 0- auto&& = clr: 0- auto&& = move(clr): 0- auto&& = rr: 0- auto&& = move(rr): 0- auto&& = crr: 1- auto&& = move(crr): 1- auto&& = Fcprv(): Dc 1-D auto&& = Fprv(): Dc 1-D auto&& = fr: 0- auto&& = move(fr): 0- const auto&& = T{8}: Pc 8-D const auto&& = T(): Dc 1-D const auto&& = move(l): 0- const auto&& = move(cl): 1- const auto&& = move(lr): 0- const auto&& = move(clr): 0- const auto&& = move(rr): 0- const auto&& = move(crr): 1- const auto&& = Fcprv(): Dc 1-D const auto&& = Fprv(): Dc 1-D const auto&& = move(fr): 0- From: T const T T& const T& T&& const T&&auto& const auto&auto&& const auto&& 8 PcD PcD - PcD PcD PcD - - - - T() DcD DcD - DcD DcD DcD - DcD DcD DcD l CcD CcD + + - - + + + - move(l) McD McD - + + + - + + + cl CcD CcD - + - - + + + - move(cl) CcD CcD - + - + + + + + lr CcD CcD + + - - + + + - move(lr) McD McD - + + + - + + + clr CcD CcD - + - - + + + - move(clr)CcD CcD - + - + + + + + rr CcD CcD - + - - + + + - move(rr) McD McD - + + + - + + + crr CcD CcD - + - - + + + - move(crr)CcD CcD - + - + + + + + Fcprv() DcD DcD - DcD - DcD DcD DcD DcD DcD Fprv() DcD DcD - DcD DcD DcD - DcD DcD DcD fr CcD CcD + + - - + + + - move(fr) McD McD - + + + - + + + T{8} - - - - - - - PcD PcD PcD DDDDD */ 

Inicializando referências constantes com objetos temporários


C ++ permite inicializar uma referência constante com um objeto temporário. Nesse caso, a vida útil de um objeto temporário será estendida. Exemplo:


 struct T { int i = 1; }; const T& t = T(); cout << ti; 

No entanto, essa extensão vitalícia funciona apenas até o final do bloco, onde o objeto temporário foi criado. Por esse motivo, se um membro de referência constante de uma classe for inicializado com um objeto temporário em um construtor, o objeto temporário será destruído no final do construtor e a referência continuará apontando para um objeto destruído, que é um comportamento indefinido:


 class A { public: // Will not compile: value-initialization of reference type //A() : t() {} const T& t; }; class B { public: // Will compile in some compilers, but temporary object will be destructed at the end of constructor B() : t(T()) { cout << "In constructor: " << ti << endl; } const T& t; }; class C { public: // Will compile, but temporary object will be destructed at the end of constructor // Address sanitizer will show the problem C() : t(std::move(T())) { cout << "In constructor: " << ti << endl; } const T& t; }; C c; cout << "C: " << cti << endl; 

Sem o desinfetante de endereços, este programa produzirá um pouco de lixo e, com o desinfetante de endereços, um erro será mostrado. Por esse motivo, esse recurso C ++ não deve ser usado ou deve ser usado com cautela.


Ligações:
Inicialização de referência
Const Referências a objetos temporários
Sobre a ligação de uma referência const a um subobjeto de um temporário




Vá para a parte 2

Source: https://habr.com/ru/post/pt479342/


All Articles