Подтвердить что ты не робот

В С++ 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. Я этого не понимаю.

4b9b3361

Ответ 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) имеет значение не, потому что для такого вызова потребуются ДВА неявных пользовательских преобразований (intfloat16 и float16Bar), и это не так допускается.

Ответ 3

Если вы реализуете его как тип struct/class, предоставьте перегрузки всех соответствующих операторов и конструкторов. Просмотрите рекомендации, необходимые для класса, если он поддерживает преобразование как в другие, так и с других типов.

Если вы хотите, чтобы ваш класс хорошо играл со стандартными шаблонами, такими как std::common_type и std::numeric_limits, вам необходимо предоставить соответствующую специализацию этих шаблонов. Вероятно, лучше всего ознакомиться с фактическими стандартами для описания требований таких специализаций, но, вероятно, есть некоторые учебные материалы (я видел хороший вводный материал для специализации std::numeric_limits, но не для std::common_type).

Всегда будут некоторые ограничения на то, как ваш тип вписывается со встроенными стандартными типами (int, float и т.д.), если вы не используете типы, специфичные для компилятора.