Информация об изменениях

Сообщение Re[3]: Амбигус оператор от 14.04.2025 11:24

Изменено 14.04.2025 11:31 rg45

Re[3]: Амбигус оператор
Здравствуйте, Marty, Вы писали:

C>>Правильный список типов для перекрывания:

C>>https://godbolt.org/z/YrhYcjKaY
C>>
C>>struct S {
C>>    S& operator<<(char              ) { std::cout<<"char              "<<std::endl; return *this; }
C>>    S& operator<<(signed char       ) { std::cout<<"signed char       "<<std::endl; return *this; }
C>>    S& operator<<(unsigned char     ) { std::cout<<"unsigned char     "<<std::endl; return *this; }
C>>    S& operator<<(signed short      ) { std::cout<<"signed short      "<<std::endl; return *this; }
C>>    S& operator<<(signed int        ) { std::cout<<"signed int        "<<std::endl; return *this; }
C>>    S& operator<<(signed long       ) { std::cout<<"signed long       "<<std::endl; return *this; }
C>>    S& operator<<(signed long long  ) { std::cout<<"signed long long  "<<std::endl; return *this; }
C>>    S& operator<<(unsigned short    ) { std::cout<<"unsigned short    "<<std::endl; return *this; }
C>>    S& operator<<(unsigned int      ) { std::cout<<"unsigned int      "<<std::endl; return *this; }
C>>    S& operator<<(unsigned long     ) { std::cout<<"unsigned long     "<<std::endl; return *this; }
C>>    S& operator<<(unsigned long long) { std::cout<<"unsigned long long"<<std::endl; return *this; }
C>>


C>>Суть: типы short и int (char и short, int и long, long и long long) могут иметь одинаковый размер, оставаясь разными типами. Тяжкое наследие Си.

C>>Типы с фиксированным размером (std::intXX_t), ссылаются на один из этих типов.
C>>Можно представить, что компилятор будет поддерживать какой-то специальный __int64_t, и std::int64_t ссылается на него, но я такого не встречал.

M>Да, в итоге так и сделал


И это правильное решение. Ибо всё это разные типы, по которым всегда можно сделать перегрузку. В то время как типы intN_t/uintN_t — это алиасы с нерегламентированным определением: https://timsong-cpp.github.io/cppwp/cstdint.syn#1.

В то же время я считаю, что ты напрасно отбрасываешь вариант с шаблонами. С шаблонами можно сделать все по-красоте. Обрати внимание на возможность перегрузок с приоритетами. Т.е. для каких-то групп типов можно предоставить общую перегрузку, а какие-то группы типов обработать более специальным образом:

https://coliru.stacked-crooked.com/a/150b460c99de60ae

#include <iostream>
#include <concepts>
#include <climits>

template <typename T>
concept Integer = std::integral<T>;

template <typename T>
concept SignedInteger = std::signed_integral<T>;

template <typename T>
concept UnsignedInteger = std::unsigned_integral<T>;

template <typename T, size_t size>
concept SignedIntegerOfSize = SignedInteger<T> and sizeof(T) * CHAR_BIT == size;

template <typename T, size_t size>
concept UnsignedIntegerOfSize = UnsignedInteger<T> and sizeof(T) * CHAR_BIT == size;

void foo(Integer auto value) {
   std::cout << "Integer: " << value << std::endl;
}

void foo(UnsignedInteger auto value) {
   std::cout << "Unsigned Integer: " << value << std::endl;
}

void foo(UnsignedIntegerOfSize<64> auto value) {
   std::cout << "Unsigned Integer(64): " << value << std::endl;
}

int main() {
   foo(42);     // Integer: 42
   foo(42U);    // Unsigned Integer: 42
   foo(42ULL);  // Unsigned Integer(64): 42
}
Re[3]: Амбигус оператор
Здравствуйте, Marty, Вы писали:

C>>Правильный список типов для перекрывания:

C>>https://godbolt.org/z/YrhYcjKaY
C>>
C>>struct S {
C>>    S& operator<<(char              ) { std::cout<<"char              "<<std::endl; return *this; }
C>>    S& operator<<(signed char       ) { std::cout<<"signed char       "<<std::endl; return *this; }
C>>    S& operator<<(unsigned char     ) { std::cout<<"unsigned char     "<<std::endl; return *this; }
C>>    S& operator<<(signed short      ) { std::cout<<"signed short      "<<std::endl; return *this; }
C>>    S& operator<<(signed int        ) { std::cout<<"signed int        "<<std::endl; return *this; }
C>>    S& operator<<(signed long       ) { std::cout<<"signed long       "<<std::endl; return *this; }
C>>    S& operator<<(signed long long  ) { std::cout<<"signed long long  "<<std::endl; return *this; }
C>>    S& operator<<(unsigned short    ) { std::cout<<"unsigned short    "<<std::endl; return *this; }
C>>    S& operator<<(unsigned int      ) { std::cout<<"unsigned int      "<<std::endl; return *this; }
C>>    S& operator<<(unsigned long     ) { std::cout<<"unsigned long     "<<std::endl; return *this; }
C>>    S& operator<<(unsigned long long) { std::cout<<"unsigned long long"<<std::endl; return *this; }
C>>


C>>Суть: типы short и int (char и short, int и long, long и long long) могут иметь одинаковый размер, оставаясь разными типами. Тяжкое наследие Си.

C>>Типы с фиксированным размером (std::intXX_t), ссылаются на один из этих типов.
C>>Можно представить, что компилятор будет поддерживать какой-то специальный __int64_t, и std::int64_t ссылается на него, но я такого не встречал.

M>Да, в итоге так и сделал


И это правильное решение. Ибо всё это разные типы, по которым всегда можно сделать перегрузку. В то время как типы intN_t/uintN_t — это алиасы с недетерминированным определением: https://timsong-cpp.github.io/cppwp/cstdint.syn#1.

В то же время я считаю, что ты напрасно отбрасываешь вариант с шаблонами. С шаблонами можно сделать все по-красоте. Обрати внимание на возможность перегрузок с приоритетами. Т.е. для каких-то групп типов можно предоставить общую перегрузку, а какие-то группы типов обработать более специальным образом:

https://coliru.stacked-crooked.com/a/150b460c99de60ae

#include <iostream>
#include <concepts>
#include <climits>

template <typename T>
concept Integer = std::integral<T>;

template <typename T>
concept SignedInteger = std::signed_integral<T>;

template <typename T>
concept UnsignedInteger = std::unsigned_integral<T>;

template <typename T, size_t size>
concept SignedIntegerOfSize = SignedInteger<T> and sizeof(T) * CHAR_BIT == size;

template <typename T, size_t size>
concept UnsignedIntegerOfSize = UnsignedInteger<T> and sizeof(T) * CHAR_BIT == size;

void foo(Integer auto value) {
   std::cout << "Integer: " << value << std::endl;
}

void foo(UnsignedInteger auto value) {
   std::cout << "Unsigned Integer: " << value << std::endl;
}

void foo(UnsignedIntegerOfSize<64> auto value) {
   std::cout << "Unsigned Integer(64): " << value << std::endl;
}

int main() {
   foo(42);     // Integer: 42
   foo(42U);    // Unsigned Integer: 42
   foo(42ULL);  // Unsigned Integer(64): 42
}