2012-02-08 2 views
10

Ecco un po 'di codice che potrebbe sembrare che avrebbe funzionato:Perché C++ numeric_limits <enum_type> :: max() == 0?

#include <cassert> 
#include <limits> 

enum test { A = 1 }; 

int main() 
{ 
    int max = std::numeric_limits<test>::max(); 
    assert(max > 0); 
} 

ma fallisce in entrambi GCC (4.6.2) e clang (2.9) su Linux: max() per i tipi enum è infatti zero! E questo rimane vero anche se si usa lo specificatore del tipo enum C++ 11 per dire esplicitamente quale tipo si vuole che l'enum abbia.

Perché è questo? E per quanto riguarda il comportamento del C++ 11, è qualcosa che viene richiesto in modo esplosivo? Non ho trovato alcuna menzione in N2347, il documento su Strongly Typed Enums.

+1

Cosa vi aspettate il valore da essere? –

+1

Qual è il risultato di is_specialized? – RaptorFactor

+0

@JamesMcNellis: suppongo che si aspetti che sia uguale a numeric_limits :: max. – RaptorFactor

risposta

22

std::numeric_limits è specializzato nella Libreria standard "per ogni tipo aritmetico, sia in virgola mobile che intero, compreso bool" (§18.3.2.1/2).

Il vostro elenco test non è uno di questi tipi, quindi viene utilizzato il modello principale. Il suo comportamento è specificato da §18.3.2.3/1: "Il modello predefinito numeric_limits<T> deve avere tutti i membri, ma con valori 0 o false".

Se volete conoscere i tratti del tipo di fondo di test, è possibile utilizzare underlying_type:

std::numeric_limits<std::underlying_type<test>::type>::max() 

In alternativa, è possibile specializzarsi numeric_limits per test e farlo ritornare i valori desiderati. Questa non è un'idea particolarmente buona, però.

+1

Perché non sta specializzando una buona idea? –

+1

@RobKennedy: Sarebbe strano al massimo e fuorviante nel peggiore dei casi. Non puoi specializzarlo una volta per tutte le enumerazioni (perché ci sono enumerazioni nella Libreria Standard), quindi dovresti specializzarlo per ogni singolo enum. Quindi, quali dovrebbero essere i valori? Non puoi davvero differire direttamente a 'underlying_type' perché il valore massimo rappresentabile dal tipo sottostante non è necessariamente il valore massimo rappresentabile dall'enumerazione (ad esempio, se' test' è rappresentato da 'int', il valore max' int' è forse '2^32-1', ma il valore massimo' test' è '1'. –

+3

Perché qualcuno dovrebbe persino specializzarlo una volta per tutte le enumerazioni? Normalmente, uno sarebbe interessato a una specializzazione per questo particolare test "Non ci sarebbe nulla di sbagliato in questo, vero? – jrok

2

Per le versioni non specializzate del modello, max restituisce T(). Non hai scritto una specializzazione numeric_limits per il tuo tipo test, in modo da ottenere l'implementazione predefinita.

1

Il numeric_limits<T> è un modello di classe normale, non è collegata al compilatore in alcun modo speciale come per scoprire enum tipi definiti dall'utente. Se si guarda il file <limits>, ha la definizione di modello predefinita che restituisce zero per tutto e un intero gruppo di specifiche specifiche del tipo per i singoli tipi, restituendo le costanti giuste.

È possibile "inserire" il proprio enum in numeric_limits fornendo una specifica di numeric_limits<test> da soli. È possibile copiare quello perda <limits> e modificarlo in base alle proprie esigenze.

1

Dal progetto C++ 11:

In 18.3.2.1, circa numeric_limits:

tipi standard

non aritmetiche, come complessi (26.4.2), non avrà specializzazioni.

E un enum non è un tipo standard aritmetico.

Poi, nel modello non specializzato:

template<class T> class numeric_limits { 
    public: 
    [...] 
    static constexpr bool is_specialized = false; 
    static constexpr T max() noexcept { return T(); } 
}; 

Cioè, la funzione non specializzata max() restituisce il valore inizializzato di default per quel tipo, cioè 0.

+0

Un enum non è effettivamente un tipo standard aritmetico, ma ciò non significa che sia un tipo standard non aritmetico. Non è affatto un tipo di tipo standard. –

+0

@RobKennedy - Oh! Hai ragione! Quel paragrafo non significa nulla in questo contesto. Ma ciò non cambia il punto principale: quel 'numeric_limits' non è specializzato per i tipi enum. – rodrigo