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 _AD
e _BC
criar datas, achei problemático devido à ordem do operador. 1974/01/06_AD
avaliaria primeiro 1974/01
(como plain int
s) e somente mais tarde o 06_AD
(para não falar de agosto e setembro tendo que ser escrito sem o 0
motivo octal). Isso pode ser contornado com a sintaxe 1974-1/6_AD
para 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
fonte
string operator "" _s(const char*s);"
não pode ser usado para analisar"hello"_s"
. Esta é uma string literal e procurará o operador com umsize_t
parâmetro adicional . Estou certo?uint16_t
cujo comportamento depende da implementação, por tipos semelhantesuwrap16
eunum16
cujo comportamento seria independente da implementação, de modo que, dadasuwrap16 w=1; unum16 n=1;
as expressõesw-2
en-2
produziria(uwrap16)65535
e(int)-1
, respectivamente [uint16_t
produziria o primeiro resultado em sistemas comint
16 bits e o segundo em sistemas comint
tamanho maior]. O maior problema que vi foi lidar com literais numéricos.sizeof
retornam tipos inteiros dependentes da implementação, mas a situação ainda pode ser muito melhor do que é. O que você pensaria desse conceito?Respostas:
Aqui está um caso em que há uma vantagem em usar literais definidos pelo usuário em vez de uma chamada de construtor:
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).
fonte
À 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 ++:
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:
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:
É 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:
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:
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.
fonte
you can write 1+2i, but you still can't write a+bi, so there's absolutely no point
Mesmo ignorando o seua+bi
exemplo é 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 escrever1.5f
e1000UL
por que não pode escrever25i
ou mesmo100101b
? 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 ++.Most of data still comes from IO
Existem 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 emx = 2 * y ;
vez dex = Two * y
ondeTwo
é 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.É 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.
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).
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.
fonte
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
s
parastd::string
ei
para 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.
fonte
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:
Entretanto, todo elemento em um modelo ou metamodelo geralmente tem um nome. Gostaríamos de escrever:
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):
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.
fonte
by the compiler at compile time
parte ... :-)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:
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.
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).
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).
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.
fonte
O suporte à verificação de dimensão em tempo de compilação é a única justificativa necessária.
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.
fonte
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
... Gostaria de saber como você expressaria isso hoje. Você teria uma classe KG e LB e compararia objetos implícitos:
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. :-)
fonte
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.
fonte
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.
fonte
using single letters in global namespace will probably be considered bad practice
Mas 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 eminline namespace
s que os usuários podem importar por atacado, se desejado.Eu usei literais de usuário para cadeias binárias como esta:
usando o
std::string(str, n)
construtor para\0
nã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::string
de um invólucrostd::vector
.fonte
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:
Não justifica esta parte:
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:
A palavra-chave 'auto' pode ser justificada, talvez apenas. Mas vamos usar apenas C ++, porque é melhor que C ++ 0x nesse aspecto.
É 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.
Talvez isso seja algo simples, mas não acredito que seja tão simples no C ++ 0x.
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.
fonte
std::complex<double> val(0, 3.14);
.