Ho definito un tipo che funge da numero intero. Voglio definire una specializzazione per std :: common_type per il mio tipo. Tuttavia, questa specializzazione dovrebbe essere in grado di fornire il common_type di bounded_integer (my class) in combinazione con qualsiasi numero di altri argomenti che sono o altri tipi di bounded_integer o built-in. Voglio che il seguente codice sia valido:Specializzazione parziale del modello di classe per un tipo visualizzato in qualsiasi posizione di un parametro del parametro modello variadiale
std::common_type<bounded_integer<1, 10>>::type
std::common_type<bounded_integer<1, 10>, int>::type
std::common_type<int, long, bounded_integer<1, 10>>::type
std::common_type<int, int, long, short, long long, short, bounded_integer<1, 10>, int, short, short, short, ..., short, bounded_integer<1, 10>>::type
Il mio primo tentativo di risolvere questo problema è stato utilizzando enable_if. Tuttavia, mi sono reso conto che questo non mi permetteva di distinguere dalla definizione biblioteca di common_type, come quello che ho avuto è stato essenzialmente
#include <type_traits>
class C {};
template<typename T, typename... Ts>
class contains_c {
public:
static constexpr bool value = contains_c<T>::value or contains_c<Ts...>::value;
};
template<typename T>
class contains_c<T> {
public:
static constexpr bool value = std::is_same<T, C>::value;
};
namespace std {
template<typename... Args, typename std::enable_if<contains_c<Args...>::value>::type>
class common_type<Args...> {
public:
using type = C;
};
} // namespace std
int main() {
}
Qualora la 'specializzazione parziale' è in realtà solo "argomenti", che non c'è più specializzato di quello che abbiamo.
Così sembra che l'unica soluzione è quella di richiedere i miei utenti di fare una delle seguenti:
- mettere sempre la bounded_integer come primo argomento a common_type
- utilizzare sempre il mio make_bounded (built-in valore intero) per convertire i loro interi in bounded_integer (quindi non avere una specializzazione di common_type per tipi built-in in combinazione con bounded_integer)
- mai mettere bounded_integer in una posizione maggiore di N, dove N è un numero determinato da I , simile al vecchio modello variadic di Visual Studio, attorno allo
3 sarebbe simile a questa:
// all_bounded_integer_or_integral and all_are_integral defined elsewhere with obvious definitions
template<intmax_t minimum, intmax_t maximum, typename... Ts, typename = type std::enable_if<all_bounded_integer_or_integral<Ts...>::value>::type>
class common_type<bounded_integer<minimum, maximum>, Ts...> {
};
template<typename T1, intmax_t minimum, intmax_t maximum, typename... Ts, typename = typename std::enable_if<all_are_integral<T1>::value>::type, typename = typename std::enable_if<all_bounded_integer_or_builtin<Ts...>::value>::type>
class common_type<T1, bounded_integer<minimum, maximum>, Ts...> {
};
template<typename T1, typename T2, intmax_t minimum, intmax_t maximum, typename... Ts, typename = typename std::enable_if<all_are_integral<T1, T2>::value>::type, typename = typename std::enable_if<all_bounded_integer_or_builtin<Ts...>::value>::type>
class common_type<T1, T2, bounded_integer<minimum, maximum>, Ts...> {
};
// etc.
c'è un modo migliore per ottenere questo risultato (modello di specializzazione, quando tutti i tipi si incontrano una condizione e uno dei tipi si incontrano un'altra condizione) per una classe che non posso cambiare la definizione originale per?
EDIT:
Sulla base delle risposte, non sono stato abbastanza chiaro nel mio problema.
In primo luogo, il comportamento atteso:
Se qualcuno chiama std :: common_type con tutti i tipi di essere un esempio di bounded_integer o di un built-in di tipo numerico, voglio che il risultato sia un bounded_integer che ha un minimo di tutti i minimi possibili e il massimo di tutti i massimi possibili.
Il problema:
ho una soluzione di lavoro quando qualcuno chiama std :: common_type su qualsiasi numero di bounded_integer. Tuttavia, se mi specializzo solo la versione a due argomenti, poi mi imbatto nel seguente problema:
std::common_type<int, unsigned, bounded_integer<0, std::numeric_limits<unsigned>::max() + 1>
dovrebbe darmi
bounded_integer<std::numeric_limits<int>::min(), std::numeric_limits<unsigned>::max() + 1>
Tuttavia, non è così.Prima applica common_type a int
e unsigned
, che segue le regole di promozione integrale standard, fornendo unsigned
. Poi si restituisce il risultato di common_type
con unsigned
e il mio bounded_integer
, dando
bounded_integer<0, std::numeric_limits<unsigned>::max() + 1>
Così aggiungendo unsigned
alla metà del parametro pacchetto, anche se dovrebbe avere assolutamente alcun impatto sul tipo di risultato (le sue gamme sono interamente contenuti negli intervalli di tutti gli altri tipi), ma influenza ancora il risultato. L'unico modo che posso pensare per evitare questo è di specializzarsi su std::common_type
per un numero qualsiasi di numeri interi integrati seguiti da bounded_integer
, seguiti da un numero qualsiasi di numeri interi incorporati o da bounded_integer
.
La mia domanda è: come posso fare questo senza doverlo approssimare scrivendo manualmente un numero arbitrario di parametri seguito da un bounded_integer
seguito da un pacchetto di parametri, o non è possibile?
EDIT 2:
La ragione per cui common_type darà valori errati può essere spiegato da questo ragionamento secondo lo standard (citando N3337)
La common_type
di int
e unsigned
è unsigned
. Per un esempio: http://ideone.com/9IxKIW. Standardese si trovano in § 20.9.7.6/3, dove il common_type
dei due valori è
typedef decltype(true ? declval<T>() : declval<U>()) type;
In § 5.16/6, si dice
il secondo e terzo operandi hanno aritmetica o tipo di enumerazione; vengono eseguite le normali conversioni aritmetiche per portarle a un tipo comune e il risultato è di quel tipo.
Le conversioni aritmetiche abituali sono definiti nel § 5/9 come
Altrimenti, se l'operando che ha senza segno tipo intero ha rango maggiore o uguale al rango del tipo dell'altro operando, l'operando con tipo intero con segno deve essere convertito nel tipo di l'operando con tipo intero senza segno.
cosa dovrebbe 'std :: common_type, short> :: type' output? 'short' o' ranged_integer :: min(), std :: numeric_limits :: max()> '. –
brunocodutra
@brunocodutra: ho aggiornato il mio post per spiegare. Mi aspetto che il risultato di 'common_type' di qualsiasi espressione che contiene tutti i tipi di' ranged_integer' e integrale sia un 'ranged_integer' con un intervallo che contenga tutti i valori possibili di qualsiasi tipo. –