В С++ 11, как я могу реализовать арифметический тип, который вписывается в иерархию встроенных типов?
В принципе, я хотел бы использовать тип float16
. Но этот вопрос заключается не в деталях, как это сделать, а в том, как настроить вещи так, чтобы мой новый тип float16 вел себя правильно с помощью float, double и всех целых типов.
Я бы хотел, чтобы мой тип float16
преобразовывался аналогично, чтобы плавать или удваивать. Например, он должен неявно использовать оба этих типа. Он также должен иметь std:: common_type (http://en.cppreference.com/w/cpp/types/common_type), который ведет себя аналогично для него, поскольку он std:: common_types ведет себя для других типов float. Это означает, что std::common_type<my_float16, float>::type = float
, std::common_type<my_float16, double>::type = double
и std::common_type<my_float16, T>::type = my_float16
, где T
- любой целочисленный тип.
Какие конструкторы и операторы литья нужно написать, чтобы сделать эту работу? Любая помощь будет оценена!
Мой другой недавний вопрос может быть связан.
EDIT: Хорошо, я построил минимальный пример, подобный Антону.
struct float16 {
explicit operator int() {
return 0;
}
operator float() {
return 0.0f;
}
};
Это имеет правильный общий тип для float и float16, но общий тип для int и float16 по-прежнему int. Я этого не понимаю.
Ответы
Ответ 1
Я собираюсь дать свой собственный ответ на этот вопрос.
Теперь я согласен с 6502, и я не думаю, что это возможно в простой форме. Единственный способ, которым я нашел это, - обеспечить перегрузку оператора для каждой возможной комбинации типов. Это можно сделать немного лучше с помощью шаблонов (и операторы Boost, http://www.boost.org/doc/libs/1_59_0/libs/utility/operators.htm, это хороший пример), но это все еще хороший объем работы и шаблонный код.
Ответ 2
Я не думаю, что вы можете получить это точно, потому что основной язык С++ рассматривает изначально определенное преобразование (например, char
to int
) по-разному от пользовательского преобразования (даже если они неявные).
Например, после
struct float16 { float16(int) {} }; // cast int->float16 is implicit
struct Foo { Foo(const double&){} }; // constructor accepts a double&
struct Bar { Bar(const float16&){} }; // constructor accepts a float16&
void foo(const Foo&) {}
void bar(const Bar&) {}
вызов foo(3)
действителен, поскольку целое число 3
может быть неявно преобразовано в double
и foo
принимает экземпляр foo
, который может быть неявным образом создан из a double
определяемым пользователем преобразование (конструктор foo
, который не является explicit
).
Однако bar(3)
имеет значение не, потому что для такого вызова потребуются ДВА неявных пользовательских преобразований (int
→ float16
и float16
→ Bar
), и это не так допускается.
Ответ 3
Если вы реализуете его как тип struct/class, предоставьте перегрузки всех соответствующих операторов и конструкторов. Просмотрите рекомендации, необходимые для класса, если он поддерживает преобразование как в другие, так и с других типов.
Если вы хотите, чтобы ваш класс хорошо играл со стандартными шаблонами, такими как std::common_type
и std::numeric_limits
, вам необходимо предоставить соответствующую специализацию этих шаблонов. Вероятно, лучше всего ознакомиться с фактическими стандартами для описания требований таких специализаций, но, вероятно, есть некоторые учебные материалы (я видел хороший вводный материал для специализации std::numeric_limits
, но не для std::common_type
).
Всегда будут некоторые ограничения на то, как ваш тип вписывается со встроенными стандартными типами (int
, float
и т.д.), если вы не используете типы, специфичные для компилятора.