Quais novos recursos os literais definidos pelo usuário adicionam ao C ++?


139

C ++ 11 introduz literais definidas pelo utilizador , que vai permitir a introdução da nova sintaxe literal baseado em literais existentes ( int, hex, string, float), de modo que qualquer tipo será capaz de ter uma apresentação literal.

Exemplos:

// imaginary numbers
std::complex<long double> operator "" _i(long double d) // cooked form
{ 
    return std::complex<long double>(0, d); 
}
auto val = 3.14_i; // val = complex<long double>(0, 3.14)

// binary values
int operator "" _B(const char*); // raw form
int answer = 101010_B; // answer = 42

// std::string
std::string operator "" _s(const char* str, size_t /*length*/) 
{ 
    return std::string(str); 
}

auto hi = "hello"_s + " world"; // + works, "hello"_s is a string not a pointer

// units
assert(1_kg == 2.2_lb); // give or take 0.00462262 pounds

À primeira vista, isso parece muito legal, mas estou me perguntando o quão aplicável é realmente, quando tentei pensar em ter os sufixos _ADe _BCcriar datas, achei problemático devido à ordem do operador. 1974/01/06_ADavaliaria primeiro 1974/01(como plain ints) e somente mais tarde o 06_AD(para não falar de agosto e setembro tendo que ser escrito sem o 0motivo octal). Isso pode ser contornado com a sintaxe 1974-1/6_ADpara que a ordem de avaliação do operador funcione, mas seja desajeitada.

Então, o que minha pergunta se resume a isso é: você acha que esse recurso se justificará? Quais outros literais você gostaria de definir que tornarão seu código C ++ mais legível?


Sintaxe atualizada para ajustar o rascunho final em junho de 2011


8
Vou votar para fechar isso. O título é claramente inflamatório.
Filhote de cachorro

76
@DeadMG, se você tiver um problema com o título, poderá editá-lo. É um pouco engraçado tentar fechar uma pergunta de 3 anos com 11 votos positivos e 8 favoritos. (Sem mencionar que a etiqueta neste site mudou nos últimos 3 anos).
Motti 28/06

5
Eu acho que você tem um erro em seus exemplos: string operator "" _s(const char*s);"não pode ser usado para analisar "hello"_s". Esta é uma string literal e procurará o operador com um size_tparâmetro adicional . Estou certo?
towi

1
Uma coisa que me perguntei é se faria sentido escrever "C portátil" em C ++, substituindo tipos como uint16_tcujo comportamento depende da implementação, por tipos semelhantes uwrap16e unum16cujo comportamento seria independente da implementação, de modo que, dadas uwrap16 w=1; unum16 n=1;as expressões w-2e n-2produziria (uwrap16)65535e (int)-1, respectivamente [ uint16_tproduziria o primeiro resultado em sistemas com int16 bits e o segundo em sistemas com inttamanho maior]. O maior problema que vi foi lidar com literais numéricos.
supercat

1
Ser capaz de ter literais numéricos interoperando sem problemas com outros tipos numéricos de comportamento definido parece permitir que esses tipos sejam usados ​​para criar uma linguagem em que o código que desejava executar ações dependentes da implementação o fizesse sem ter que confiar na implementação - comportamentos definidos. Existem alguns lugares onde o BID ainda será inevitável porque coisas como diferenças de ponteiro e sizeofretornam tipos inteiros dependentes da implementação, mas a situação ainda pode ser muito melhor do que é. O que você pensaria desse conceito?
Supercat 01/05

Respostas:


71

Aqui está um caso em que há uma vantagem em usar literais definidos pelo usuário em vez de uma chamada de construtor:

#include <bitset>
#include <iostream>

template<char... Bits>
  struct checkbits
  {
    static const bool valid = false;
  };

template<char High, char... Bits>
  struct checkbits<High, Bits...>
  {
    static const bool valid = (High == '0' || High == '1')
                   && checkbits<Bits...>::valid;
  };

template<char High>
  struct checkbits<High>
  {
    static const bool valid = (High == '0' || High == '1');
  };

template<char... Bits>
  inline constexpr std::bitset<sizeof...(Bits)>
  operator"" _bits() noexcept
  {
    static_assert(checkbits<Bits...>::valid, "invalid digit in binary string");
    return std::bitset<sizeof...(Bits)>((char []){Bits..., '\0'});
  }

int
main()
{
  auto bits = 0101010101010101010101010101010101010101010101010101010101010101_bits;
  std::cout << bits << std::endl;
  std::cout << "size = " << bits.size() << std::endl;
  std::cout << "count = " << bits.count() << std::endl;
  std::cout << "value = " << bits.to_ullong() << std::endl;

  //  This triggers the static_assert at compile time.
  auto badbits = 2101010101010101010101010101010101010101010101010101010101010101_bits;

  //  This throws at run time.
  std::bitset<64> badbits2("2101010101010101010101010101010101010101010101010101010101010101_bits");
}

A vantagem é que uma exceção em tempo de execução é convertida em um erro em tempo de compilação. Você não pode adicionar a declaração estática ao bitset ctor usando uma string (pelo menos não sem argumentos de modelo de string).


7
Você pode fazer o mesmo dando ao std :: bitset um construtor constexpr adequado.
precisa saber é o seguinte

1
@NicolBolas Você está certo. Estou realmente surpreso que alguém não esteja lá. Talvez devêssemos propor um ou dois para 2014 se não for tarde demais.
EMSR

192

À primeira vista, parece ser um simples açúcar sintático.

Mas, olhando mais fundo, vemos que é mais do que açúcar sintático, pois estende as opções do usuário do C ++ para criar tipos definidos pelo usuário que se comportam exatamente como tipos internos distintos. Nisso, esse pequeno "bônus" é uma adição muito interessante do C ++ 11 ao C ++.

Nós realmente precisamos dele em C ++?

Vejo poucos usos no código que escrevi nos últimos anos, mas só porque não o usei em C ++ não significa que não seja interessante para outro desenvolvedor de C ++ .

Tínhamos usado no C ++ (e no C, eu acho), literais definidos pelo compilador, para digitar números inteiros como inteiros curtos ou longos, números reais como float ou double (ou mesmo long double) e seqüências de caracteres como caracteres normais ou largos .

Em C ++, tivemos a possibilidade de criar nossos próprios tipos (ou seja, classes), potencialmente sem sobrecarga (embutidos, etc.). Tivemos a possibilidade de adicionar operadores aos seus tipos, para que eles se comportassem como tipos internos semelhantes, o que permite que os desenvolvedores de C ++ usem matrizes e números complexos tão naturalmente quanto teriam se eles fossem adicionados à própria linguagem. Podemos até adicionar operadores de conversão (o que geralmente é uma má idéia, mas às vezes é a solução certa).

Ainda perdemos uma coisa em que os tipos de usuário se comportam como tipos internos: literais definidos pelo usuário.

Então, acho que é uma evolução natural para a linguagem, mas para ser o mais completo possível: " Se você deseja criar um tipo e se comportar o máximo possível dos tipos internos, aqui estão as ferramentas. .. "

Eu acho que é muito semelhante à decisão do .NET de transformar todas as primitivas em uma estrutura, incluindo booleanos, números inteiros etc., e todas as estruturas derivam de Object. Essa decisão por si só coloca o .NET muito além do alcance do Java ao trabalhar com primitivos, independentemente de quanto hacks de boxe / unboxing o Java adicionará à sua especificação.

Você realmente precisa dele em C ++?

Esta pergunta é para você responder. Não Bjarne Stroustrup. Não Herb Sutter. Nenhum membro do comitê padrão de C ++. É por isso que você tem a opção em C ++ , e eles não restringem uma notação útil apenas aos tipos internos.

Se você precisar, é uma adição bem-vinda. Se você não fizer isso, bem ... não usá-lo. Não vai custar nada.

Bem-vindo ao C ++, o idioma em que os recursos são opcionais.

Inchado??? Mostre-me seus complexos !!!

Há uma diferença entre inchado e complexo (trocadilhos).

Como mostrado por Niels em Quais novos recursos os literais definidos pelo usuário adicionam ao C ++? , poder escrever um número complexo é um dos dois recursos adicionados "recentemente" a C e C ++:

// C89:
MyComplex z1 = { 1, 2 } ;

// C99: You'll note I is a macro, which can lead
// to very interesting situations...
double complex z1 = 1 + 2*I;

// C++:
std::complex<double> z1(1, 2) ;

// C++11: You'll note that "i" won't ever bother
// you elsewhere
std::complex<double> z1 = 1 + 2_i ;

Agora, o tipo C99 "duplo complexo" e o tipo C ++ "std :: complex" podem ser multiplicados, adicionados, subtraídos, etc., usando sobrecarga do operador.

Porém, no C99, eles acabaram de adicionar outro tipo como um tipo interno e suporte de sobrecarga de operador interno. E eles adicionaram outro recurso literal embutido.

Em C ++, eles apenas usaram recursos existentes da linguagem, viram que o recurso literal era uma evolução natural da linguagem e, portanto, a adicionaram.

Em C, se você precisar do mesmo aprimoramento de notação para outro tipo, ficará sem sorte até o lobby para adicionar suas funções de ondas quânticas (ou pontos 3D, ou qualquer tipo básico que esteja usando em seu campo de trabalho) ao O padrão C como um tipo interno é bem-sucedido.

No C ++ 11, você pode fazer isso sozinho:

Point p = 25_x + 13_y + 3_z ; // 3D point

Está inchado? Não , a necessidade existe, como mostra como os complexos C e C ++ precisam de uma maneira de representar seus valores complexos literais.

É projetado incorretamente? Não , ele foi projetado como qualquer outro recurso C ++, com extensibilidade em mente.

É apenas para fins de notação? Não , pois pode até adicionar segurança de tipo ao seu código.

Por exemplo, vamos imaginar um código orientado a CSS:

css::Font::Size p0 = 12_pt ;       // Ok
css::Font::Size p1 = 50_percent ;  // Ok
css::Font::Size p2 = 15_px ;       // Ok
css::Font::Size p3 = 10_em ;       // Ok
css::Font::Size p4 = 15 ;         // ERROR : Won't compile !

É muito fácil impor uma forte digitação à atribuição de valores.

É perigoso?

Boa pergunta. Essas funções podem ter espaço para nome? Se sim, então Jackpot!

De qualquer forma, como tudo, você pode se matar se uma ferramenta for usada incorretamente . C é poderoso, e você pode atirar na cabeça se usar mal a pistola C. O C ++ possui a pistola C, mas também o bisturi, o taser e qualquer outra ferramenta que você encontrará no kit de ferramentas. Você pode usar mal o bisturi e sangrar até a morte. Ou você pode criar um código muito elegante e robusto.

Então, como todo recurso C ++, você realmente precisa dele? É a pergunta que você deve responder antes de usá-lo em C ++. Caso contrário, não lhe custará nada. Mas se você realmente precisa, pelo menos, o idioma não o decepcionará.

O exemplo da data?

Parece-me que seu erro é que você está misturando operadores:

1974/01/06AD
    ^  ^  ^

Isso não pode ser evitado, porque / como operador, o compilador deve interpretá-lo. E, AFAIK, é uma coisa boa.

Para encontrar uma solução para o seu problema, eu escreveria o literal de alguma outra maneira. Por exemplo:

"1974-01-06"_AD ;   // ISO-like notation
"06/01/1974"_AD ;   // french-date-like notation
"jan 06 1974"_AD ;  // US-date-like notation
19740106_AD ;       // integer-date-like notation

Pessoalmente, eu escolheria o número inteiro e as datas ISO, mas isso depende das SUAS necessidades. Qual é o objetivo de deixar o usuário definir seus próprios nomes literais.


1
Obrigado, eu e minhas outras personalidades alternativas fomos descobertos. Mais seriamente, eu escrevi isso sozinho, mas talvez eu esteja usando alguma expressão da minha língua nativa e eles não se traduzem bem para o inglês.
22468 paercebal

Quanto às "partes diferentes", como mostram seus títulos, desculpe, acho que isso organiza um post bastante longo. Quanto ao texto em negrito, é o resumo do parágrafo em que estão inseridos. Pessoas que desejam apenas as informações sem justificativa podem limitar sua leitura nos títulos e no texto em negrito.
28468 Paercebal

3
+1. Explicação muito boa. Estamos esperando que isso seja implementado. É realmente importante para nós. Trabalhamos no MDE (Model-Driven Engineering) e consideramos isso necessário. Estou adicionando uma resposta abaixo para explicar nosso caso.
Diego Sevilla

9
@TGV: you can write 1+2i, but you still can't write a+bi, so there's absolutely no pointMesmo ignorando o seu a+biexemplo é ridículo, o fato de você o perceber como "baixa frequência" não significa que todo mundo o faz. . . Olhando para o quadro geral, o objetivo é garantir que os objetos definidos pelo usuário possam ser, tanto quanto possível, considerados cidadãos de primeira classe do idioma, assim como os tipos incorporados. Então, se você pode escrever 1.5fe 1000ULpor que não pode escrever 25iou mesmo 100101b? Ao contrário de C e Java, os tipos de usuário não devem ser considerados cidadãos de segunda classe da linguagem em C ++.
paercebal

3
@Anton:: Most of data still comes from IOExistem muitos valores codificados no código. Veja todos os booleanos, todos os números inteiros, todas as duplas que aparecem no código, porque é mais conveniente escrever em x = 2 * y ;vez de x = Two * yonde Twoé uma constante de tipo forte . Os literais definidos pelo usuário nos permitem colocar um tipo nele e escrever: x = 2_speed * y ;e fazer com que o compilador verifique se o cálculo faz sentido. . . Tudo isso acontece com uma digitação forte. . . Talvez você não o use. Mas com certeza o farei, assim que puder usar um compilador habilitado para C ++ 11 no trabalho.
paercebal

36

É muito bom para código matemático. Fora da minha cabeça, posso ver o uso dos seguintes operadores:

deg para graus. Isso torna a escrita em ângulos absolutos muito mais intuitiva.

double operator ""_deg(long double d)
{ 
    // returns radians
    return d*M_PI/180; 
}

Também pode ser usado para várias representações de ponto fixo (que ainda estão em uso no campo de DSP e gráficos).

int operator ""_fix(long double d)
{ 
    // returns d as a 1.15.16 fixed point number
    return (int)(d*65536.0f); 
}

Estes parecem bons exemplos de como usá-lo. Eles ajudam a tornar constantes no código mais legíveis. É outra ferramenta para tornar o código ilegível também, mas já temos tantas ferramentas abusadas que mais uma não dói muito.


1
"mas já temos tantas ferramentas abusadas que mais uma não dói muito. " Uau, espero que essa não seja a filosofia por trás de todo esse recurso c ++ [x] 1234567890 que inundou recentemente. Imagine ter que aprender uma biblioteca que usa todos aqueles, além de formatos de arquivo de dez para configuração e duas ferramentas para pré e pós-processamento de você código ...
masterxilo

@masterxilo Obviamente, na realidade, seu exemplo é um absurdo: UDLs não são mais difíceis de aprender do que funções, como apenas açúcar de sintaxe para elas - além disso, qualquer boa lib usa apenas os recursos necessários para melhorar a UX - e documenta exatamente o que é todos os meios. Se alguém usa demais um recurso para gerar código ilegível (supondo que seja evitável em sua linha de trabalho ...), ele não coloca esse recurso em falha, apenas o uso. Além disso, uma pessoa ilegível é o pão com manteiga de outra. São todas as opiniões - e opções . Se você não gosta deles, não se preocupe! Você não precisa usá-los. Outros podem .
underscore_d

17

As UDLs têm espaço para nome (e podem ser importadas usando declarações / diretivas, mas você não pode explicitamente nomear um espaço para nome como literal 3.14std::i), o que significa que (espero) não haverá muitos confrontos.

O fato de que eles podem realmente ser modelados (e conscritos) significa que você pode fazer algumas coisas bem poderosas com UDLs. Os autores de Bigint ficarão realmente felizes, pois eles podem finalmente ter constantes arbitrariamente grandes, calculadas em tempo de compilação (via constexpr ou templates).

Só estou triste por não vermos alguns literais úteis no padrão (pelo que parece), como spara std::stringe ipara a unidade imaginária.

A quantidade de tempo de codificação que será salva pelas UDLs na verdade não é tão alta, mas a legibilidade será amplamente aumentada e mais e mais cálculos poderão ser alterados para o tempo de compilação para uma execução mais rápida.


Obrigado por esclarecer a questão sobre namespaces ... Eu estava pensando nisso.
Nathan Reed

12

Deixe-me adicionar um pouco de contexto. Para o nosso trabalho, literais definidos pelo usuário são muito necessários. Trabalhamos em MDE (Model-Driven Engineering). Queremos definir modelos e metamodelos em C ++. Na verdade, implementamos um mapeamento do Ecore para o C ++ ( EMF4CPP ).

O problema surge quando é possível definir elementos de modelo como classes em C ++. Estamos adotando a abordagem de transformar o metamodelo (Ecore) em modelos com argumentos. Os argumentos do modelo são as características estruturais de tipos e classes. Por exemplo, uma classe com dois atributos int seria algo como:

typedef ::ecore::Class< Attribute<int>, Attribute<int> > MyClass;

Entretanto, todo elemento em um modelo ou metamodelo geralmente tem um nome. Gostaríamos de escrever:

typedef ::ecore::Class< "MyClass", Attribute< "x", int>, Attribute<"y", int> > MyClass;

MAS, C ++ ou C ++ 0x não permitem isso, pois cadeias de caracteres são proibidas como argumentos para modelos. Você pode escrever o nome char por char, mas isso é uma bagunça. Com literais definidos pelo usuário, poderíamos escrever algo semelhante. Digamos que usamos "_n" para identificar os nomes dos elementos do modelo (não uso a sintaxe exata, apenas para criar uma idéia):

typedef ::ecore::Class< MyClass_n, Attribute< x_n, int>, Attribute<y_n, int> > MyClass;

Finalmente, ter essas definições como modelos nos ajuda muito a projetar algoritmos para percorrer os elementos do modelo, transformações de modelos etc. que são realmente eficientes, porque informações de tipo, identificação, transformações etc. são determinadas pelo compilador no momento da compilação.


4
Eu gosto muito muito a by the compiler at compile timeparte ... :-)
paercebal

12

Bjarne Stroustrup fala sobre UDLs nesta palestra em C ++ 11 , na primeira seção sobre interfaces ricas em tipos, em torno de 20 minutos.

Seu argumento básico para UDLs assume a forma de um silogismo:

  1. Tipos "triviais", ou seja, tipos primitivos internos, podem capturar apenas erros de tipo triviais. As interfaces com tipos mais ricos permitem ao sistema de tipos capturar mais tipos de erros.

  2. Os tipos de erros de tipo que um código ricamente digitado pode capturar têm impacto no código real. (Ele dá o exemplo da Mars Climate Orbiter, que falhou infame devido a um erro de dimensões em uma constante importante).

  3. No código real, as unidades raramente são usadas. As pessoas não as usam, porque incorrer em computação em tempo de execução ou sobrecarga de memória para criar tipos ricos é muito caro e o uso de código de unidade com modelo C ++ preexistente é tão notoriamente feio que ninguém a usa. (Empiricamente, ninguém o usa, mesmo que as bibliotecas existam há uma década).

  4. Portanto, para que os engenheiros usem unidades em código real, precisávamos de um dispositivo que (1) não incorre em sobrecarga de tempo de execução e (2) seja notadamente aceitável.


9

O suporte à verificação de dimensão em tempo de compilação é a única justificativa necessária.

auto force = 2_N; 
auto dx = 2_m; 
auto energy = force * dx; 

assert(energy == 4_J); 

Veja, por exemplo, PhysUnits-CT-Cpp11 , uma pequena biblioteca somente de cabeçalho C ++ 11, C ++ 14 para análise dimensional em tempo de compilação e manipulação e conversão de unidades / quantidades. Mais simples que o Boost.Units , suporta literais de símbolos de unidade , como m, g, s, prefixos métricos como m, k, M, depende apenas da biblioteca C ++ padrão, apenas SI, de poderes integrais de dimensões.


Ou consulte unidades , uma biblioteca dimensional de análise dimensional e conversão de unidades , em tempo de compilação, somente cabeçalho, construída em c ++ 14 sem dependências de Nic Holthaus .
Martin Moene 29/09/16

6

Hmm ... ainda não pensei sobre esse recurso. Sua amostra foi bem pensada e certamente é interessante. O C ++ é muito poderoso como é agora, mas, infelizmente, a sintaxe usada nos trechos de código que você lê às vezes é excessivamente complexa. A legibilidade é, se não tudo, pelo menos muito. E esse recurso seria voltado para maior legibilidade. Se eu pegar o seu último exemplo

assert(1_kg == 2.2_lb); // give or take 0.00462262 pounds

... Gostaria de saber como você expressaria isso hoje. Você teria uma classe KG e LB e compararia objetos implícitos:

assert(KG(1.0f) == LB(2.2f));

E isso faria também. Com tipos que têm nomes mais longos ou tipos que você não tem esperanças de ter um construtor tão bom para sem escrever um adaptador, pode ser uma boa adição para criação e inicialização implícita de objetos em tempo real. Por outro lado, você já pode criar e inicializar objetos usando métodos também.

Mas eu concordo com Nils em matemática. As funções trigonométricas C e C ++, por exemplo, requerem entrada em radianos. Eu acho que em graus, portanto, uma conversão implícita muito curta como a Nils postada é muito boa.

Por fim, no entanto, será um açúcar sintático, mas terá um pequeno efeito na legibilidade. E provavelmente será mais fácil escrever algumas expressões também (sin (180.0deg) é mais fácil escrever do que sin (deg (180.0)). E haverá pessoas que abusarão do conceito, mas as pessoas que abusam da linguagem devem usar linguagens muito restritivas, em vez de algo tão expressivo quanto C ++.

Ah, meu post diz basicamente nada, exceto: vai ficar tudo bem, o impacto não será muito grande. Não vamos nos preocupar. :-)


5
Seus parênteses estão desequilibrados! Desculpe, meu TOC também me odeia.
X-Istence 26/10/08

3

Eu nunca precisei ou quis esse recurso (mas esse poderia ser o efeito Blub ). Minha reação instintiva é que é coxo e provavelmente atrairá as mesmas pessoas que acham legal sobrecarregar o operador + para qualquer operação que remotamente possa ser interpretada como uma adição.


Eu confirmo: Artigo muito interessante.
26468 paercebal

2

O C ++ geralmente é muito rigoroso quanto à sintaxe usada - exceto no pré-processador, não há muito que você possa usar para definir uma sintaxe / gramática personalizada. Por exemplo, podemos sobrecarregar os operatos existentes, mas não podemos definir novos - na IMO isso está muito sintonizado com o espírito do C ++.

Não me importo de algumas maneiras de obter um código-fonte mais personalizado - mas o ponto escolhido parece muito isolado para mim, o que mais me confunde.

Mesmo o uso pretendido pode tornar muito mais difícil a leitura do código-fonte: uma única letra pode ter efeitos colaterais de amplo alcance que de forma alguma podem ser identificados a partir do contexto. Com simetria para u, lef, a maioria dos desenvolvedores escolherá letras únicas.

Isso também pode transformar o escopo em um problema, o uso de letras únicas no espaço para nome global provavelmente será considerado uma prática ruim e as ferramentas que supostamente misturam bibliotecas mais facilmente (espaços para nome e identificadores descritivos) provavelmente anularão seu objetivo.

Vejo algum mérito em combinação com "auto", também em combinação com uma biblioteca de unidades , como unidades de impulso , mas não o suficiente para merecer essa adição.

Eu me pergunto, no entanto, que idéias inteligentes surgimos.


1
using single letters in global namespace will probably be considered bad practiceMas isso não tem relevância: (A) UDLs devem ser definidas no escopo do espaço para nome (não global) ... presumivelmente porque (B) devem consistir em um sublinhado, em seguida,> = 1 letra, não apenas na letra e esses identificadores em o NS global está reservado para a implementação. Isso é pelo menos 2 pontos contra a ideia de que as UDLs geram confusão de forma inata. Quanto ao escopo do namespace, reduzindo a utilidade do recurso, é por isso que, por exemplo, o stdlib os declara em inline namespaces que os usuários podem importar por atacado, se desejado.
underscore_d

2

Eu usei literais de usuário para cadeias binárias como esta:

 "asd\0\0\0\1"_b

usando o std::string(str, n)construtor para \0não cortar a corda ao meio. (O projeto trabalha muito com vários formatos de arquivo.)

Isso foi útil também quando me esqueci std::stringde um invólucro std::vector.


-5

O ruído de linha nessa coisa é enorme. Também é horrível de ler.

Deixe-me saber, eles raciocinaram essa nova adição de sintaxe com algum tipo de exemplo? Por exemplo, eles têm alguns programas que já usam C ++ 0x?

Para mim, esta parte:

auto val = 3.14_i

Não justifica esta parte:

std::complex<double> operator ""_i(long double d) // cooked form
{ 
    return std::complex(0, d);
}

Nem mesmo se você usasse a sintaxe i em 1000 outras linhas também. Se você escreve, provavelmente escreve 10000 linhas de outra coisa ao longo disso também. Especialmente quando você ainda provavelmente escreverá principalmente em toda parte isso:

std::complex<double> val = 3.14i

A palavra-chave 'auto' pode ser justificada, talvez apenas. Mas vamos usar apenas C ++, porque é melhor que C ++ 0x nesse aspecto.

std::complex<double> val = std::complex(0, 3.14);

É como ... tão simples. Mesmo pensando que todos os suportes std e pontudos são coxos se você usá-lo em qualquer lugar. Não começo a adivinhar qual sintaxe existe no C ++ 0x para transformar std :: complex em complexo.

complex = std::complex<double>;

Talvez isso seja algo simples, mas não acredito que seja tão simples no C ++ 0x.

typedef std::complex<double> complex;

complex val = std::complex(0, 3.14);

Possivelmente? > :)

Enfim, o ponto é: escrever 3.14i em vez de std :: complex (0, 3.14); em geral, você não economiza muito tempo, exceto em alguns casos super especiais.


10
@ Cheery: Para você, "auto val = 3.14i" não justifica o código escrito para suportá-lo. Eu poderia responder que, para mim, "printf ("% i ", 25)" não justifica o código escrito para printf. Você vê um padrão?
22468 paercebal

5
@ Cheery: "O ruído da linha nessa coisa é enorme". Não, não é ... "Também é horrível ler". Seu argumento subjetivo é interessante, mas você deve dar uma olhada sobrecarga de operador em geral para ver o código para este recurso está longe de surpreender / chocante ... Para um desenvolvedor C ++
paercebal

3
auto ajudará a legibilidade. considere usar interadores em um loop for: for (auto it = vec.begin (); it! = vec.end (); ++ it) ... eu sei sobre for_each, mas não gosto de criar um functor para usá-lo .
KitsuneYMG

1
@kts: Com C ++ 1x, teremos lambda e intervalo para
Joe D

3
Se sua linha de C ++ é melhor que C ++ 0x, então minha linha é melhor ainda. Escreve apenas: std::complex<double> val(0, 3.14);.
Ben Voigt
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.