Сообщение Re[3]: Амбигус оператор от 14.04.2025 11:24
Изменено 14.04.2025 11:36 rg45
Re[3]: Амбигус оператор
Здравствуйте, Marty, Вы писали:
C>>Правильный список типов для перекрывания:
C>>https://godbolt.org/z/YrhYcjKaY
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
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>>Суть: типы 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
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
}