operator "" nel moderno C ++

1

Ho preso un esempio che ho trovato on-line in cui un constexpr del modulo _binary poteva essere valutato al momento della compilazione come unsigned long long e quindi ho provato a generalizzarlo per qualsiasi base da 2 a 36. Ad esempio, 17b1234_baseChange avrebbe essere valutato dalla base 17 come ((1 * 17 +2) * 17 + 3) * 17 + 4 = 5624.

Mi rendo conto che questo è un esempio forzato, ma mi chiedevo quali fossero i limiti per l'operatore "". Sembra il compilatore quando parser crea token separati per numeri e lettere. Ero curioso di sapere se si trattava di un bug o di un comportamento previsto. (Oltre a "b", ho provato altri separatori senza fortuna.) Grazie per la tua comprensione.

Riga di comando all'interno di link : g ++ - 4.9 -std = c ++ 14 -O3 -Wall -Wextra -pedantic -pthread -pedantic-errors main.cpp -lm & & ./a.out

Codice:

#include <iostream>

const char Delimiter = 'b';
typedef unsigned long long ULL;

/// Extract one "digit" from a digit string and then recurse.
template<unsigned short NumericBase, char Head, char... Rest> 
struct baseChange_helper
{
  constexpr ULL operator()(ULL result) const;
};

/// Teminate recursion when interpreting a numeric string.
template<unsigned short NumericBase, char Head> struct     
baseChange_helper<NumericBase, Head>
{
   constexpr ULL operator()(ULL result) const;
};

template<unsigned short NumericBase, char Head, char... Rest>
constexpr ULL baseChange_helper<NumericBase, Head, Rest...>::operator()
(ULL result) const
{
   static_assert(   (Head >= '0' && (Head <= '0' + std::min(NumericBase-1, 9)))
                 || (NumericBase > 10 && (Head >= 'A' && Head <= 'A' +     
                     std::min(NumericBase-10, 25)))
                 , "not a valid number in this base");
    return baseChange_helper<Rest...>{}(result = result * (NumericBase -1) 
                         + ((Head > 'A') ? (10 + Head - 'A') : (Head - '0')));
}

template<unsigned short NumericBase, char Head> 
constexpr ULL
baseChange_helper<NumericBase, Head>::operator()(ULL result) const
{
    static_assert(   (Head >= '0' && (Head <= '0' + std::min(NumericBase-1, 9)))
                 || (NumericBase > 10 && (Head >= 'A' && 
                     Head <= 'A' + std::min(NumericBase-10, 25)))
                 , "not a valid number in this base");
   return  result * (NumericBase -1)  + 
           ((Head > 'A') ? (10 + Head - 'A') : (Head - '0'));
}

template<unsigned short NumericBase, char Head, char... Rest> struct     
baseChange_parser
{
   constexpr ULL operator()() const;
};
template<unsigned short NumericBase, char Head, char... Rest>
constexpr ULL baseChange_parser<NumericBase, Head, Rest...>::operator()()
const
{
   static_assert( (Head == Delimiter && NumericBase > 1 && NumericBase < 36) ||
                  (Head >= '0' && Head <= '9'),
                  "not a valid base");    
   return ( Head == Delimiter ? baseChange_helper<NumericBase, Rest...>((ULL)0)
                    : baseChange_parser<NumericBase*10 + Head - '0', Rest...>()
          );
}
template<char... Chars> constexpr ULL operator"" _baseChange()
{
   return baseChange_parser<0, Chars...>{}();
}

int main() {
    const ULL v = 17b1234_baseChange;
    std::cout << v << std::endl;
}
    
posta Grandpa Scorpion 07.02.2015 - 03:55
fonte

2 risposte

2

L'input per il tuo operatore letterale definito dall'utente è una delle due cose: o è un numero, è una stringa.

Affinché sia un numero, deve essere qualcosa che il lexer / parser C ++ normale può riconoscere come un numero. Legge il testo del programma, converte quel token in un numero e passa il numero alla tua routine.

Altrimenti, può essere una stringa. In questo caso, è necessario racchiudere la stringa tra virgolette, proprio come qualsiasi altra stringa letterale. Per quanto riguarda il compilatore, è solo una stringa letterale in modo che possa essere formattata in qualsiasi modo tu scelga purché i suoi contenuti siano caratteri legittimi (ad esempio, se contiene un back-slash, il prossimo carattere deve formare una delle escape standard sequenze, a meno che non si utilizzi un valore letterale stringa raw).

Poiché 17b1234 non è qualcosa che il compilatore può / riconoscerà come un numero, non può essere passato a un operatore letterale definito dall'utente come un numero. Se insisti davvero su questo formato, sei praticamente bloccato nel passarlo come una stringa:

"17b1234"_baseChange

In questo caso, il tuo operatore (ovviamente) dovrà ricevere il suo argomento come una stringa, non un numero.

    
risposta data 10.02.2015 - 21:01
fonte
0

Non penso che questo dovrebbe funzionare. Il sistema funziona sulle rappresentazioni di stringa di token individuali, quindi, poiché nell'esempio "17b12345_baseChange" la "b" non è un carattere accettabile per un valore letterale intero (vedi nota sotto) il token viene diviso in quel punto e l'operazione di cambio di base vede solo "12345", lasciando "17b" dietro per generare un errore di sintassi.

Mi chiedo in che modo questo interagisca con i caratteri di raggruppamento di cifre di C ++ a 14, tuttavia potresti far capire "17'12345", forse?

Nota: tranne nella stringa "0b" che è consentita solo all'inizio del letterale

    
risposta data 07.02.2015 - 11:05
fonte

Leggi altre domande sui tag