O estilo de sintaxe do tipo de retorno à direita deve se tornar o padrão para novos programas C ++ 11? [fechadas]

95

C ++ 11 suporta uma nova sintaxe de função:

auto func_name(int x, int y) -> int;

Atualmente, esta função seria declarada como:

int func_name(int x, int y);

O novo estilo não parece ter sido amplamente adotado ainda (digamos no gcc stl)

No entanto, esse novo estilo deve ser preferido em todos os novos programas C ++ 11 ou será usado apenas quando necessário?

Pessoalmente, prefiro o estilo antigo quando possível, mas uma base de código com estilos mistos parece muito feia.

mirk
fonte
30
Está lá principalmente para decltypeargumentos.
Cat Plus Plus
o que CatPlusPlus diz: não faz muito sentido usá-lo em seu exemplo
stijn
@Cat Plus Plus Isso significa que você deixa as coisas como estão em C ++ 03, a menos que precise derivar o tipo de retorno.
mirk
1
É feio ter que especificar "auto" antes de cada função. É como a resposta atrevida do C ++ para o "def" do python?
Erik Aronesty

Respostas:

112

Existem certos casos em que você deve usar um tipo de retorno à direita. Mais notavelmente, um tipo de retorno lambda, se especificado, deve ser especificado por meio de um tipo de retorno à direita. Além disso, se o seu tipo de retorno utiliza um decltypeque requer que os nomes dos argumentos estejam no escopo, um tipo de retorno final deve ser usado (no entanto, pode-se usar normalmente declval<T>para contornar este último problema).

O tipo de retorno à direita tem algumas outras vantagens secundárias. Por exemplo, considere uma definição de função de membro não embutida usando a sintaxe de função tradicional:

struct my_awesome_type
{
    typedef std::vector<int> integer_sequence;

    integer_sequence get_integers() const;
}; 

my_awesome_type::integer_sequence my_awesome_type::get_integers() const
{
    // ...
}

Os typedefs de membros não estão no escopo até que o nome da classe apareça antes ::get_integers, então temos que repetir a qualificação da classe duas vezes. Se usarmos um tipo de retorno final, não precisamos repetir o nome do tipo:

auto my_awesome_type::get_integers() const -> integer_sequence
{
    // ...
}

Neste exemplo, não é um grande negócio, mas se você tiver nomes de classe longos ou funções de membro de modelos de classe que não são definidos inline, isso pode fazer uma grande diferença na legibilidade.

Em sua sessão "Fresh Paint" no C ++ Now 2012, Alisdair Meredith apontou que se você usar tipos de retorno à direita de forma consistente, os nomes de todas as suas funções se alinham perfeitamente:

auto foo() -> int;
auto bar() -> really_long_typedef_name;

Eu usei tipos de retorno à direita em todos os lugares em CxxReflect , então se você estiver procurando por um exemplo de como o código fica usando-os consistentemente, você pode dar uma olhada lá (por exemplo, a typeclasse ).

James McNellis
fonte
1
Não parece haver um consenso ainda, mas é interessante olhar para o CxxReflect com o novo estilo.
mirk
Olá James. Essa resposta provavelmente poderia ser mais precisa à luz do padrão C ++ 14.
Drew Dormann
@DrewDormann O que você adicionaria / mudaria?
underscore_d
1
O alinhamento é na verdade uma grande vantagem, a ponto de desejar que houvesse uma nova palavra-chave 'func' para substituir a sem sentido 'auto' aqui.
Johan Boulé
68

Além do que outros disseram, o tipo de retorno à direita também permite o uso this, o que não é permitido

struct A {
  std::vector<int> a;

  // OK, works as expected
  auto begin() const -> decltype(a.begin()) { return a.begin(); }

  // FAIL, does not work: "decltype(a.end())" will be "iterator", but 
  // the return statement returns "const_iterator"
  decltype(a.end()) end() const { return a.end(); }
};

Na segunda declaração, usamos o estilo tradicional. No entanto, como thisnão é permitido nessa posição, o compilador não o usa implicitamente. Então, o a.end()usa o tipo declarado estaticamente de apara determinar qual endsobrecarga vector<int>dele vai chamar, que acaba sendo a versão não const.

Johannes Schaub - litb
fonte
3
Embora seja uma demonstração boa / simples do conceito (usando membros em tipos de retorno), é engraçado, pois em C ++ 14 a especificação de um tipo é totalmente redundante em uma definição embutida sem conversão; agora podemos usar apenas a dedução do tipo de retorno total. : P
sublinhado_d
28

Outra vantagem é que a sintaxe do tipo de retorno à direita pode ser mais legível quando a função retorna um ponteiro para uma função. Por exemplo, compare

void (*get_func_on(int i))(int);

com

auto get_func_on(int i) -> void (*)(int);

No entanto, pode-se argumentar que uma melhor legibilidade pode ser alcançada simplesmente introduzindo um alias de tipo para o ponteiro de função:

using FuncPtr = void (*)(int);
FuncPtr get_func_on(int i);
s3rvac
fonte
10

Veja este bom artigo: http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html Muito bom exemplo de quando usar esta sintaxe sem decltype no jogo :

class Person
{
public:
    enum PersonType { ADULT, CHILD, SENIOR };
    void setPersonType (PersonType person_type);
    PersonType getPersonType ();
private:
    PersonType _person_type;
};

auto Person::getPersonType () -> PersonType
{
    return _person_type;
}

E uma explicação brilhante também roubada do artigo de Alex Allain "Como o valor de retorno vai no final da função, em vez de antes dele, você não precisa adicionar o escopo da classe."

Compare com este possível caso quando alguém por acidente se esquece do escopo da classe e, para um desastre maior, outro PersonType é definido no escopo global:

typedef float PersonType; // just for even more trouble
/*missing: Person::*/
PersonType Person::getPersonType ()
{
    return _person_type;
}
PiotrNycz
fonte
7
Não tenho certeza se isso se enquadra na categoria "desastre": se o tipo estiver errado, o código não compilará. Os erros de tempo de execução podem ter consequências desastrosas; erros de tempo de compilação, nem tanto.
James McNellis
4
@JamesMcNellis compare a saída do compilador: prog.cpp:13:12: error: prototype for 'PersonType Person::getPersonType()' does not match any in class 'Person'vs. prog.cpp:13:1: error: 'PersonType' does not name a type O primeiro erro do compilador é, pelo menos para mim, pior de entender.
PiotrNycz
Pessoalmente, não concordo, acho a segunda mensagem mais difícil de ler e prefiro que a implementação se pareça com a declaração.
jrh