O que significa a instrução “return {}” em C ++ 11?

115

O que significa a declaração

return {};

em C ++ 11 indicar e quando usá-lo em vez de (digamos)

return NULL;

ou

return nullptr;
Pedia
fonte
59
ele retorna uma instância construída por padrão do tipo de retorno da função.
Richard Hodges,
Ou é simples return;sem valor?
i486 de
Não, como a discussão revela, é um erro em tempo de compilação se sua função retornar algo (ou seja, não do tipo de retorno nulo) e você escrever apenas. return; Por outro lado, return{};é válido se você tiver um tipo de retorno.
Pedia
@Pedia Nem sempre, algum objeto exigirá argumentos para ser construído
MM

Respostas:

108

return {};indica "retornar um objeto do tipo de retorno da função inicializado com um inicializador de lista vazio ". O comportamento exato depende do tipo do objeto retornado.

De cppreference.com (como o OP está marcado como C ++ 11, excluí as regras em C ++ 14 e C ++ 17; consulte o link para obter mais detalhes):

  • Se a lista de inicialização com chaves estiver vazia e T for um tipo de classe com um construtor padrão, a inicialização de valor é executada.
  • Caso contrário, se T for um tipo de agregado, a inicialização do agregado é executada.
  • Caso contrário, se T é uma especialização de std :: initializer_list, o objeto T é inicializado diretamente ou inicializado por cópia, dependendo do contexto, da lista de inicialização com chaves.
  • Caso contrário, são considerados os construtores de T, em duas fases:

    • Todos os construtores que tomam std :: initializer_list como o único argumento, ou como o primeiro argumento se os argumentos restantes tiverem valores padrão, são examinados e combinados pela resolução de sobrecarga contra um único argumento do tipo std :: initializer_list
    • Se o estágio anterior não produzir uma correspondência, todos os construtores de T participam da resolução de sobrecarga contra o conjunto de argumentos que consiste nos elementos da lista de inicialização com chaves, com a restrição de que apenas conversões não restritivas são permitidas. Se este estágio produz um construtor explícito como a melhor correspondência para uma inicialização de lista de cópias, a compilação falha (observe, na inicialização de cópia simples, construtores explícitos não são considerados).
  • Caso contrário (se T não for um tipo de classe), se a lista de inicialização com chaves tiver apenas um elemento e T não for um tipo de referência ou for um tipo de referência compatível com o tipo do elemento, T é direto inicializado (em inicialização de lista direta) ou inicializado por cópia (em inicialização de lista de cópia), exceto que conversões estreitas não são permitidas.

  • Caso contrário, se T for um tipo de referência que não é compatível com o tipo do elemento. (falha se a referência for uma referência de valor não constante)
  • Caso contrário, se a lista de inicialização com chaves não tiver elementos, T é inicializado com valor.

Antes do C ++ 11, para uma função que retorna a std::string, você teria escrito:

std::string get_string() {
    return std::string();
}

Usando a sintaxe de chaves em C ++ 11, você não precisa repetir o tipo:

std::string get_string() {
    return {}; // an empty string is returned
}

return NULLe return nullptrdeve ser usado quando a função retorna um tipo de ponteiro:

any_type* get_pointer() {
    return nullptr;
}

No entanto, NULLestá obsoleto desde C ++ 11 porque é apenas um alias para um valor inteiro (0), enquanto nullptré um tipo de ponteiro real:

int get_int() {
    return NULL; // will compile, NULL is an integer
}

int get_int() {
    return nullptr; // error: nullptr is not an integer
}
rgmt
fonte
91

Isso provavelmente é confuso:

int foo()
{
  return {};   // honestly, just return 0 - it's clearer
}

Provavelmente não é:

SomeObjectWithADefaultConstructor foo()
{
  return {};
  // equivalent to return SomeObjectWithADefaultConstructor {};
}
Richard Hodges
fonte
9
Portanto, é um erro em tempo de compilação se o tipo de retorno não tiver um construtor padrão, correto?
Pedia
10
É um erro de compilação se o tipo de retorno for uma classe que não possui um construtor padrão não explícito e não é um agregado.
Oktalista de
3
Se o tipo tiver um initializer_listconstrutor, ele não seria usado se nenhum construtor padrão estivesse disponível?
celtschk
4
"provavelmente confuso"? É por isso que alguma alma sem nome se referiu a "Aquela obscenidade inchada que é C ++"? Qualquer economia em pressionamentos de tecla que isso fornece possivelmente justificar o potencial de falta de clareza que oferece? Esta é uma pergunta sincera. Por favor, me convença com exemplos práticos.
MickeyfAgain_BeforeExitOfSO
4
return {}NÃO é equivalente areturn SomeObjectWithADefaultConstructor{};
MM
26

return {};significa que {}é o inicializador para o valor de retorno . O valor de retorno é inicializado em lista com uma lista vazia.


Aqui estão algumas informações básicas sobre o valor de retorno , com base em [stmt.return] no padrão C ++:

Para uma função que retorna por valor (ou seja, o tipo de retorno não é uma referência e não void), existe um objeto temporário chamado valor de retorno . Este objeto é criado pela returninstrução e seus inicializadores dependem do que estava na instrução return.

O valor de retorno sobrevive até o final da expressão completa no código que chamou a função; se tiver tipo de classe, seu destruidor será executado, a menos que tenha o tempo de vida estendido pelo chamador ligando uma referência diretamente a ele.

O valor de retorno pode ser inicializado de duas maneiras diferentes:

  • return some_expression;- o valor de retorno é inicializado com cópia desome_expression
  • return { possibly_empty_list };- o valor de retorno é inicializado em lista a partir da lista.

Supondo que Tseja o tipo de retorno da função, observe que return T{};é diferente de return {}: no primeiro, um temporário T{}é criado e, em seguida, o valor de retorno é inicializado por cópia desse temporário.

Não será possível compilar se Tnão houver um construtor de cópia / movimentação acessível, mas return {};terá êxito mesmo se esses construtores não estiverem presentes. Consequentemente, return T{};pode mostrar efeitos colaterais do construtor de cópia, etc., embora este seja um contexto de elisão de cópia, então pode não mostrar.


Aqui está uma breve recapitulação da inicialização de lista em C ++ 14 (N4140 [dcl.init.list] / 3), onde o inicializador é uma lista vazia:

  • Se Tfor um agregado, então cada membro é inicializado a partir de seu inicializador de chave ou igual, se houver um, caso contrário, como se por {} (portanto, aplique essas etapas recursivamente).
  • Se Tfor um tipo de classe com um construtor padrão fornecido pelo usuário, esse construtor é chamado.
  • Se Tfor um tipo de classe com um = defaultconstrutor padrão definido implicitamente ou ed, o objeto será inicializado com zero e o construtor padrão será chamado.
  • Se Tfor um std::initializer_list, o valor de retorno é uma lista vazia.
  • Caso contrário (ou Tseja, não é um tipo de classe - os tipos de retorno não podem ser matrizes), o valor de retorno é inicializado com zero.
MILÍMETROS
fonte
O init agregado vem primeiro e inicializa recursivamente cada membro com {}, que pode ou não ser valor-init.
TC
@TC certo, eu fui por cppreference, mas esqueci um "até C ++ 14"
MM
3

É uma espécie de atalho para uma nova instância do tipo de retorno de métodos.

Victor Mwenda
fonte