Está capturando um objeto recém-construído por um comportamento indefinido const ref

11

O seguinte (exemplo artificial) está bom ou é um comportamento indefinido:

// undefined behavior?
const auto& c = SomeClass{};

// use c in code later
const auto& v = c.GetSomeVariable();
Samaursa
fonte

Respostas:

12

É seguro. Const ref prolonga a vida útil temporária. O escopo será o escopo de const ref.

O tempo de vida de um objeto temporário pode ser estendido vinculando-se a uma referência de valor constante ou a uma referência de valor nominal (desde C ++ 11). Consulte a inicialização de referência para obter detalhes.

Sempre que uma referência é vinculada a um temporário ou a um subobjeto, o tempo de vida do temporário é estendido para corresponder ao tempo de vida da referência, com as seguintes exceções :

  • um limite temporário para um valor de retorno de uma função em uma instrução de retorno não é estendido: é destruído imediatamente no final da expressão de retorno. Essa função sempre retorna uma referência pendente.
  • um limite temporário a um membro de referência em uma lista de inicializadores de construtores persiste apenas até a saída do construtor, desde que o objeto exista. (nota: essa inicialização está incorreta a partir de DR 1696).
  • existe um limite temporário para um parâmetro de referência em uma chamada de função até o final da expressão completa que contém essa chamada de função: se a função retornar uma referência que sobrevive à expressão completa, ela se tornará uma referência pendente.
  • existe um limite temporário para uma referência no inicializador usado em uma nova expressão até o final da expressão completa que contém essa nova expressão, desde que o objeto inicializado. Se o objeto inicial sobreviver à expressão completa, seu membro de referência se tornará uma referência pendente.
  • existe um limite temporário para uma referência em um elemento de referência de um agregado inicializado usando a sintaxe de inicialização direta (parênteses), em oposição à sintaxe de inicialização da lista (chaves) até o final da expressão completa que contém o inicializador. struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference

Em geral, o tempo de vida de um temporário não pode ser estendido ainda mais "transmitindo-o": uma segunda referência, inicializada a partir da referência à qual o temporário estava vinculado, não afeta seu tempo de vida.

como apontou @Konrad Rudolph (e veja o último parágrafo acima):

"Se c.GetSomeVariable()retornar uma referência a um objeto local ou a uma referência de que ele próprio está estendendo a vida útil de algum objeto, a extensão da vida útil não será ativada"

Esquecimento
fonte
11
Você deve citar a fonte dessa citação.
Corridas de leveza em órbita
@LightnessRaceswithMonica done. Eu estava procurando por um texto melhor.
Oblivion
2
Seria bom sublinhar que isso é verdade apenas para valores . Se c.GetSomeVariable()retornar uma referência a um objeto local ou uma referência de que ele próprio está estendendo a vida útil de algum objeto, a extensão da vida útil não entra em ação.
Konrad Rudolph
@KonradRudolph Thanks! Eu adicionei a exceção também.
Oblivion
4

Não deve haver problema aqui, graças à extensão da vida útil . O objeto recém-construído sobreviverá até que a referência fique fora do escopo.

Brian
fonte
3

Sim, isso é perfeitamente seguro: a ligação a uma constreferência estende a vida útil do temporário ao escopo dessa referência.

Observe que o comportamento não é transitivo . Por exemplo, com

const auto& cc = []{
    const auto& c = SomeClass{};
    return c;
}();

cc oscilações.

Bathsheba
fonte
2

Isso é seguro.

[class.temporary]/5: Existem três contextos nos quais os temporários são destruídos em um ponto diferente do final da expressão completa . [..]

[class.temporary]/6: O terceiro contexto é quando uma referência é vinculada a um objeto temporário. O objeto temporário ao qual a referência está vinculada ou o objeto temporário que é o objeto completo de um subobjeto ao qual a referência está vinculada persiste pelo tempo de vida da referência, se o valor de referência ao qual a referência está vinculada foi obtido através de um dos seguintes : [muitas coisas aqui]

Raças de leveza em órbita
fonte
1

É seguro neste caso específico. Observe, no entanto, que nem todos os temporários são seguros para capturar por referência const ... por exemplo

#include <stdio.h>

struct Foo {
    int member;

    Foo() : member(0) {
        printf("Constructor\n");
    }

    ~Foo() {
        printf("Destructor\n");
    }

    const Foo& method() const {
        return *this;
    }
};

int main() {
    {
        const Foo& x = Foo{};        // safe
        printf("here!\n");
    }
    {
        const int& y = Foo{}.member; // safe too (special rule for this)
        printf("here (2)!\n");
    }
    {
        const Foo& z = Foo{}.method(); // NOT safe
        printf("here (3)!\n");
    }
    return 0;
}

A referência obtida para zNÃO é segura de usar, porque a instância temporária será destruída no final da expressão completa, antes de chegar à printfinstrução. A saída é:

Constructor
here!
Destructor
Constructor
here (2)!
Destructor
Constructor
Destructor
here (3)!
6502
fonte