¿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;
16
sornbro 10 may. 2019 a las 00:12

5 respuestas

La mejor respuesta

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.

6
Lightness Races in Orbit 9 may. 2019 a las 23:59

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.

4
Claas Bontus 10 may. 2019 a las 08:37

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.

-2
julio 14 may. 2019 a las 13:50

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
-4
Mirko 10 may. 2019 a las 00:31

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
4
Jim Rogers 12 may. 2019 a las 19:57