¿C ++ ofrece algo similar a subtype
de Ada para limitar un tipo?
P.ej.:
type Weekday is (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday);
subtype Working_Day is Weekday range Monday .. Friday;
5 respuestas
No, no de forma nativa.
Lo que describa podría representarse mejor como una enumeración de ámbito, acompañada de una enumeración de ámbito separada con un subconjunto de enumeraciones que comparten representaciones numéricas con la enumeración de ámbito "principal".
Podrías definir aún más algunas conversiones entre los dos, pero sin reflexión no es realmente posible hacerlo todo elegante e intuitivo, al menos no sin codificar y duplicar un montón de cosas que más bien frustran el propósito.
Sería mejor, al programar C ++, intentar abandonar por completo la mentalidad imbuida por la programación en otros lenguajes.
Dicho esto, esta es realmente una buena idea, ¡aunque no aguantaría la respiración!
Solución alternativa: solo use una enumeración y aplique la verificación de rango donde lo necesite.
Lo que desea podría (al menos parcialmente) realizarse utilizando std::variant
introducido con C ++ 17.
struct Monday {};
struct Tuesday {};
/* ... etc. */
using WeekDay= std::variant<Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday>;
El siguiente código define sub_variant_t
que construye un nuevo variant
a partir del tipo enviado. P.ej. using Working_Day= sub_variant_t<WeekDay,5>;
toma los primeros cinco elementos de Weekday
.
template<class T,size_t o,class S>
struct sub_variant_h;
template<class T,size_t o,size_t... I>
struct sub_variant_h<T,o,std::index_sequence<I...> >
{
using type= std::variant<typename std::variant_alternative_t<(I+o),T>... >;
};
template<class T,size_t end, size_t beg=0>
struct sub_variant
{
using type= typename sub_variant_h<T,beg,std::make_index_sequence<end-beg> >:type;
};
template<class T,size_t end, size_t beg=0>
using sub_variant_t = typename sub_variant<T,end,beg>::type;
Si desea copiar valores del tipo más pequeño (Working_Day
) al más grande (Weekday
) puede usar WeekDay d3= var2var<WeekDay>( d1 );
donde var2var
se define de la siguiente manera.
template<class toT, class... Types>
toT
var2var( std::variant<Types...> const & v )
{
return std::visit([](auto&& arg) -> toT {return toT(arg);}, v);
}
Vea esto livedemo.
Probablemente pueda sobrecargar la asignación con una condición posterior
Ensures(result > 0 && result < 10);
Puramente teórico. No lo he probado yo mismo. ¿Pero qué piensan ustedes?
Pero es divertido apreciar cómo cada actualización en C ++ empujan como características avanzadas todas las cosas que los programadores de Ada dan por sentado.
La verificación de rango tiene un costo. C ++ tiene una política de costo cero para las funciones: si desea la función y debe pagar un costo, debe ser explícito. Dicho esto, la mayoría de las veces puedes usar alguna biblioteca o escribir la tuya.
Además, ¿qué esperas cuando alguien intenta poner Sunday
en Working_Day
? ¿Una excepción (lo más probable)? Para configurarlo en Monday
? Para configurarlo en Friday
? ¿Invalidar el objeto? ¿Mantener el mismo valor e ignorar eso (mala idea)?
Como ejemplo:
#include <iostream>
#include <string>
using namespace std;
enum class Weekday
{
Sunday= 0,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
};
template <class T, T min, T max>
class RangedAccess
{
static_assert(max >= min, "Error min > max");
private:
T t;
public:
RangedAccess(const T& value= min)
{
*this= value;
}
RangedAccess& operator=(const T& newValue)
{
if (newValue > max || newValue < min) {
throw string("Out of range");
}
t= newValue;
}
operator const T& () const
{
return t;
}
const T& get() const
{
return t;
}
};
using Working_Day= RangedAccess<Weekday, Weekday::Monday, Weekday::Friday>;
int main()
{
Working_Day workday;
cout << static_cast<int>(workday.get()) << endl; // Prints 1
try {
workday= Weekday::Tuesday;
cout << static_cast<int>(workday.get()) << endl; // Prints 2
workday= Weekday::Sunday; // Tries to assign Sunday (0), throws
cout << static_cast<int>(workday.get()) << endl; // Never gets executed
} catch (string s) {
cout << "Exception " << s << endl; // Prints "Exception out of range"
}
cout << static_cast<int>(workday.get()) << endl; // Prints 2, as the object remained on Tuesday
}
Que produce:
1
2
Exception Out of range
2
Hay algunas diferencias adicionales entre las enumeraciones de C ++ y las enumeraciones de Ada. El siguiente código de Ada demuestra algunas de estas diferencias.
with Ada.Text_IO; use Ada.Text_IO;
procedure Subtype_Example is
type Days is (Monday, Tueday, Wednesday, Thursday, Friday, Saturday, Sunday);
subtype Work_Days is Days range Monday..Friday;
begin
Put_Line("Days of the week:");
for D in Days'Range loop
Put_Line(D'Image);
end loop;
New_Line;
Put_Line("Days with classification:");
for D in Days'Range loop
Put(D'Image & " is a member of");
if D in Work_Days then
Put_Line(" Work_Days");
else
Put_Line(" a non-work day");
end if;
end loop;
end Subtype_Example;
La salida de este programa es:
Days of the week:
MONDAY
TUEDAY
WEDNESDAY
THURSDAY
FRIDAY
SATURDAY
SUNDAY
Days with classification:
MONDAY is a member of Work_Days
TUEDAY is a member of Work_Days
WEDNESDAY is a member of Work_Days
THURSDAY is a member of Work_Days
FRIDAY is a member of Work_Days
SATURDAY is a member of a non-work day
SUNDAY is a member of a non-work day
El subtipo Work_Days tiene una relación is-a con el tipo Days. Todos los miembros de Work_Days también son miembros de Days. En este ejemplo, el conjunto de valores válidos para Work_Days es un subconjunto del conjunto de valores válidos para Days.
Los caracteres en Ada se definen como una enumeración. Por lo tanto, es simple definir subtipos del tipo Carácter para usos especiales. El siguiente ejemplo lee el texto de un archivo y cuenta el número de ocurrencias de letras mayúsculas y minúsculas, ignorando todos los otros caracteres en el archivo.
with Ada.Text_IO; use Ada.Text_IO;
procedure Count_Letters is
subtype Upper_Case is Character range 'A'..'Z';
subtype Lower_Case is Character range 'a'..'z';
Uppers : array(Upper_Case) of Natural;
Lowers : array(Lower_Case) of Natural;
File_Name : String(1..1024);
File_Id : File_Type;
Length : Natural;
Line : String(1..100);
begin
-- set the count arrays to zero
Uppers := (Others => 0);
Lowers := (Others => 0);
Put("Enter the name of the file to read: ");
Get_Line(Item => File_Name,
Last => Length);
-- Open the named file
Open(File => File_Id,
Mode => In_File,
Name => File_Name(1..Length));
-- Read the file one line at a time
while not End_Of_File(File_Id) loop
Get_Line(File => File_Id,
Item => Line,
Last => Length);
-- Count the letters in the line
for I in 1..Length loop
if Line(I) in Upper_Case then
Uppers(Line(I)) := Uppers(Line(I)) + 1;
elsif Line(I) in Lower_Case then
Lowers(Line(I)) := Lowers(Line(I)) + 1;
end if;
end loop;
end loop;
Close(File_Id);
-- Print the counts of upper case letters
for Letter in Uppers'Range loop
Put_Line(Letter'Image & " =>" & Natural'Image(Uppers(Letter)));
end loop;
-- print the counts of lower case letters
for Letter in Lowers'Range loop
Put_Line(Letter'Image & " =>" & Natural'Image(Lowers(Letter)));
end loop;
end Count_Letters;
Se definen dos subtipos de caracteres. El subtipo Upper_Case contiene el rango de valores de caracteres desde 'A' hasta 'Z', mientras que el subtipo Lower_Case contiene el rango de valores de caracteres desde 'a' hasta 'z'.
Se crean dos matrices para contar las letras leídas. La matriz Uppers está indexada por el conjunto de valores Upper_Case. Cada elemento de la matriz es una instancia de Natural, que es un subtipo predefinido de Entero que contiene solo valores no negativos. La matriz Lowers está indexada por el conjunto de valores Lower_Case. Cada elemento de Lowers es también una instancia de Natural.
El programa solicita un nombre de archivo, abre ese archivo y luego lee el archivo línea por línea. Los caracteres en cada línea se analizan. Si el carácter es un carácter Upper_Case, el elemento de matriz en la parte superior indexado por la letra analizada se incrementa. Si el carácter es un carácter Lower_Case, el elemento de matriz en Lowers indexado por la letra analizada se incrementa.
El siguiente resultado es el resultado de leer el archivo fuente para el programa count_letters.
Enter the name of the file to read: count_letters.adb
'A' => 3
'B' => 0
'C' => 12
'D' => 0
'E' => 2
'F' => 13
'G' => 2
'H' => 0
'I' => 21
'J' => 0
'K' => 0
'L' => 36
'M' => 1
'N' => 9
'O' => 7
'P' => 4
'Q' => 0
'R' => 3
'S' => 2
'T' => 3
'U' => 9
'V' => 0
'W' => 0
'X' => 0
'Y' => 0
'Z' => 1
'a' => 51
'b' => 3
'c' => 8
'd' => 19
'e' => 146
'f' => 15
'g' => 16
'h' => 22
'i' => 50
'j' => 0
'k' => 0
'l' => 38
'm' => 13
'n' => 57
'o' => 48
'p' => 35
'q' => 0
'r' => 62
's' => 41
't' => 78
'u' => 19
'v' => 0
'w' => 12
'x' => 2
'y' => 6
'z' => 2
Preguntas vinculadas
Nuevas preguntas
c++
C ++ es un lenguaje de programación de propósito general. Originalmente fue diseñado como una extensión de C, y tiene una sintaxis similar, pero ahora es un lenguaje completamente diferente. Use esta etiqueta para preguntas sobre el código (que se compilará) con un compilador de C ++. Utilice una etiqueta específica de la versión para preguntas relacionadas con una revisión estándar específica [C ++ 11], [C ++ 14], [C ++ 17] o [C ++ 20] etc.