Переопределил operator<< для своего потока, для всех std::intN_t/uintN_t
При выводе unsigned long говорит, что оператор амбигус: "error C2593: 'operator <<' is ambiguous", и перечисляет все int* перегрузки, и ни одна ему не нравится больше других
Глянул, как они определены в MSVC
typedef signed char int8_t;
typedef short int16_t;
typedef int int32_t;
typedef long long int64_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
Как видно, unsigned long тут нет. Можно было бы добавить перегрузку для unsigned long, но в другом компиляторе может быть всё по другому, и unsigned long уже будет в этом списке. Как тут быть?
Можно шаблон конечно сделать для целых типов, но пока хотелось бы решить без этого
Здравствуйте, Marty, Вы писали:
M>>>Можно шаблон конечно сделать для целых типов, но пока хотелось бы решить без этого _>>namespace ? M>Что неймспейс? https://godbolt.org/z/WGYabPK6r
то что ваш стрим не обязан быть в std
M>typedef — это просто алиас к существующему типу, перегрузки компилятор видит по оригинальному типа, пофик на алиас.
и что?
Здравствуйте, Marty, Вы писали:
M>Не обязан, и не в std. И что? Я не очень понимаю мысль
Тогда уточните что вам надо
M>И то, что если сделать перегрузку для std::uint64_t, компилятор будет видеть её как перегрузку для unsigned long long
Так потому как это один и тот же тип.
Если надо сделать его как-то иначе выводить сделайте обёртку и явно указывайте что вам надо.
std::uint64_t v1; unsigned long long v2;
stream << OutputWrapper1(v1) << OutputWrapper2(v2);
Здравствуйте, kov_serg, Вы писали:
M>>Не обязан, и не в std. И что? Я не очень понимаю мысль _>Тогда уточните что вам надо
Да вроде в стартовом посте написано вполне доходчиво
Я полагал, что перегрузок по std:(u)intXX_t хватит, чтобы покрыть все варианты интов, но оказалось, что нет. И на другом компиляторе тайпдефы могут быть другими, и если я под MSVC допилю недостающие, то с другим компилятором будет по-другому.
M>>И то, что если сделать перегрузку для std::uint64_t, компилятор будет видеть её как перегрузку для unsigned long long _>Так потому как это один и тот же тип.
Так я вроде про это и говорил, а ты про какие-то namespace
_>Если надо сделать его как-то иначе выводить сделайте обёртку и явно указывайте что вам надо. _>
_>std::uint64_t v1; unsigned long long v2;
_>stream << OutputWrapper1(v1) << OutputWrapper2(v2);
_>
Здравствуйте, Marty, Вы писали:
M>Да вроде в стартовом посте написано вполне доходчиво
нет
M>Я полагал, что перегрузок по std:(u)intXX_t хватит, чтобы покрыть все варианты интов, но оказалось, что нет. И на другом компиляторе тайпдефы могут быть другими, и если я под MSVC допилю недостающие, то с другим компилятором будет по-другому.
А нафига вы их все перегружаете?
Здравствуйте, kov_serg, Вы писали:
_>нафига вы их все перегружаете?
Ну вот мне тоже регулярно требуется разная реализация операций для 16-, 32- и 64-разрядных значений, ибо их вычислительная сложность заметно отличается. Пока операции используются непосредственно, и значения передаются типизированными переменными/константами, все хорошо. Как требуется выполнять операции над литералами, и/или обернуть их в шаблоны — начинается кошмар из-за integral promotions, запретить которые нет никакой возможности. И приходится костылить, на результат невозможно смотреть без отвращения.
Здравствуйте, Marty, Вы писали:
M>Здравствуйте!
M>Переопределил operator<< для своего потока, для всех std::intN_t/uintN_t
M>При выводе unsigned long говорит, что оператор амбигус: "error C2593: 'operator <<' is ambiguous", и перечисляет все int* перегрузки, и ни одна ему не нравится больше других
M>Глянул, как они определены в MSVC M>
M>typedef signed char int8_t;
M>typedef short int16_t;
M>typedef int int32_t;
M>typedef long long int64_t;
M>typedef unsigned char uint8_t;
M>typedef unsigned short uint16_t;
M>typedef unsigned int uint32_t;
M>typedef unsigned long long uint64_t;
M>
M>Как видно, unsigned long тут нет. Можно было бы добавить перегрузку для unsigned long, но в другом компиляторе может быть всё по другому, и unsigned long уже будет в этом списке. Как тут быть?
M>Можно шаблон конечно сделать для целых типов, но пока хотелось бы решить без этого
struct S {
S& operator<<(char ) { std::cout<<"char "<<std::endl; return *this; }
S& operator<<(signed char ) { std::cout<<"signed char "<<std::endl; return *this; }
S& operator<<(unsigned char ) { std::cout<<"unsigned char "<<std::endl; return *this; }
S& operator<<(signed short ) { std::cout<<"signed short "<<std::endl; return *this; }
S& operator<<(signed int ) { std::cout<<"signed int "<<std::endl; return *this; }
S& operator<<(signed long ) { std::cout<<"signed long "<<std::endl; return *this; }
S& operator<<(signed long long ) { std::cout<<"signed long long "<<std::endl; return *this; }
S& operator<<(unsigned short ) { std::cout<<"unsigned short "<<std::endl; return *this; }
S& operator<<(unsigned int ) { std::cout<<"unsigned int "<<std::endl; return *this; }
S& operator<<(unsigned long ) { std::cout<<"unsigned long "<<std::endl; return *this; }
S& operator<<(unsigned long long) { std::cout<<"unsigned long long"<<std::endl; return *this; }
Суть: типы short и int (char и short, int и long, long и long long) могут иметь одинаковый размер, оставаясь разными типами. Тяжкое наследие Си.
Типы с фиксированным размером (std::intXX_t), ссылаются на один из этих типов.
Можно представить, что компилятор будет поддерживать какой-то специальный __int64_t, и std::int64_t ссылается на него, но я такого не встречал.
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 ссылается на него, но я такого не встречал.
Здравствуйте, Chorkov, Вы писали:
C>Суть: типы short и int (char и short, int и long, long и long long) могут иметь одинаковый размер, оставаясь разными типами. Тяжкое наследие Си.
Можно еще вот так извернуться если не нравиться разные но одинаковые типы https://godbolt.org/z/4ocTWszE5
C>Типы с фиксированным размером (std::intXX_t), ссылаются на один из этих типов. C>Можно представить, что компилятор будет поддерживать какой-то специальный __int64_t, и std::int64_t ссылается на него, но я такого не встречал.
вот что харектерно, c++ всё есть для того чтобы иметь bigint но std::bigint нет.
Зато еще есть куча всяких целых спец типов __m128i __m256i __m512i
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.
В то же время я считаю, что ты напрасно отбрасываешь вариант с шаблонами. С шаблонами можно сделать все по-красоте. Обрати внимание на возможность перегрузок с приоритетами. Т.е. для каких-то групп типов можно предоставить общую перегрузку, а какие-то группы типов обработать более специальным образом:
R>И это правильное решение. Ибо всё это разные типы, по которым всегда можно сделать перегрузку. В то время как типы intN_t/uintN_t — это алиасы с нерегламентированным определением: https://timsong-cpp.github.io/cppwp/cstdint.syn#1.
Да, я сначала не очень хорошо подумал, когда делал через std::u/intXX_t
Казалось красиво и единообразно, и по идее, должно было покрывать всё.
R>В то же время, я считаю, что ты напрасно отбрасываешь вариант с шаблонами. С шаблонами можно сделать все по красоте. Обрати внимание на возможность перегрузок с приоритетами. Т.е. для каких-то групп типов можно предоставить общую перегрузку, а какие-то группы типов обработать поотдельности:
Ну, у меня и другие типы, кроме интов используются, и, если говорить о других местах, то там и так шаблоны, не хотелось делать что у части шаблонов список шаблонных параметров другой. Плюс, мне надо чтобы работало ADL, с шаблонами может что-то сломаться (предполагается, что пользователь может использовать свои типы, и должен в своём NS определить для типа шаблонную функцию martyFormatValueFormat, где параметр шаблона тип строки, в которую надо конвертнуть значение пользовательского типа).
А по красоте с разными группами типов как раз особенно и не надо городить.
Здравствуйте, Marty, Вы писали:
M>Ну, у меня и другие типы, кроме интов используются, и, если говорить о других местах, то там и так шаблоны, не хотелось делать что у части шаблонов список шаблонных параметров другой.
По-моему, это довод в пользу использования шаблонов, а не отказа от них. Шаблоны же для того и существуют, чтобы помогать работать с разнообразыми системами типов эффективно и без дублирования кода.
M>Плюс, мне надо чтобы работало ADL, с шаблонами может что-то сломаться (предполагается, что пользователь может использовать свои типы, и должен в своём NS определить для типа шаблонную функцию martyFormatValueFormat, где параметр шаблона тип строки, в которую надо конвертнуть значение пользовательского типа).
Шаблоны сами по себе не ломают ADL. ADL — это про то, где и как компилятор будет искать кандидата для подстановки, а не про то, как кандидаты определены. ADL же отталикивается от типов фактических параметров. Поломаться если и может, то только от кривых рук.
--
Справедливость выше закона. А человечность выше справедливости.
_>>template<typename T> using is_bool = std::is_same<std::remove_cv_t<T>, bool>;
_>>
M>Спасибо!
Здесь уместно будет заюзать std::decay дабы полностью очистить тип от разного рода модификаторов, в т.ч. и ссылок, и тем самым избавиться от лишнего геморроя при работе с формальными параметрами функций:
template<typename T> using is_bool = std::is_same<std::decay_t<T>, bool>;
А ещё все вот эти метафунции std::is_... канули в прошлое с приходом концептов. То же самое, но по-современному:
template<typename T> concept Bool = std::same_as<std::decay_t<T>, bool>;
static_assert(Bool<const bool&>);
void foo(Bool auto b) {/* . . . */}
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R>А ещё все вот эти метафунции std::is_... канули в прошлое с приходом концептов. То же самое, но по-современному:
Не не, пока только C++17 по многим причинам (в тч и потому, что в тех же линупсах дефолтный компилер не самый свежий)
А SFINAE был уже в C++03, но я тогда не прочувствовал его в полной мере, сам тогда такого кода почти не писал, только использовал, часто не понимая, как оно работает, да и хелперов из C++11 — C++17 не было, сейчас хочу прочувствовать всё это полной ложкой
Я не на переднем крае (cutting edge), я в своё время долго писал на C++03, даже после выхода C++11, а) бесовщина какая-то, б) ждал пока станет буль менее мейнстримом в) да и вообще часто другими вещами занимался, сипипи был в фоне