C ++: приведение для пользовательских типов - PullRequest
1 голос
/ 07 октября 2009

Как я могу получить ту же обработку приведения типов для пользовательских типов, что и встроенную, например:

float a = 5.4;
std::string s = a;//error, no conversion avaible
int x = a;//warning, possible data loss
int y = (int)a;//fine
int z = static_cast<int>a;//fine
float b = c;//warning, possible data loss

Теперь скажите, что у меня есть свои собственные классы Int и Float, как мне получить те же ошибки и предупреждения?

class Int
{
public:
    int value;
    Int(int v);
    ...
};
class Float
{
public:
    float value;
    Float(float v);
    ...
};
Int c = 10;
Float a = 5.5;
std::string s = a;
Int x = a;
Int y = (Int)a;
Int z = static_cast<Int>a;
Float b = c;

Мне известно о создании перегруженных операторов приведения и использовании конструкторов, однако я не знаю, как заставить это работать правильно для неявных и явных приведений, например, рассмотреть. Если я не добавляю явные приведения в эти методы, тогда я получаю предупреждение, когда они скомпилированы, но не при их вызове, и если я это делаю, то я не получаю сообщение об ошибке в коде классов, но все равно не получаю предупреждение при использовании либо.

Я предполагаю, что есть какой-то способ пометить оператор приведения как явный, чтобы генерировалось предупреждение, если он пытается привести неявно, но не с явным (C-Style или static_cast) приведением)

EDIT: Хорошо, я думаю, что получаю это для случаев, подобных этому, когда все рассматриваемые типы полностью известны, но как насчет случаев, когда один или оба являются шаблонами, и что ни один из типов не отображается на встроенный тип?

template<typename T> class Vector2
{
public:
    T x, y;
    Vector2():x(0),y(0){}
    Vector2(T x, T y):x(x),y(y){}

    //Works as expected, warning if adding T and T2 is unsafe, or error if
    //incompatible*/
    template<typename T2>Vector2<T>& operator += (const Vector2<T2> &v);
    //Also works as desired
    Vector2<T>& operator *= (T s);

    //allows the conversion, but always generates warnings if 
    //T and T2 would, even if this constructor is used by an explicit
    //case. How can I suppress the warnings for the explicit cast, but
    //not for implicit casts?
    template<typename T2>Vector2(const Vector2<T2> &v);//uses implicit conversion form T2 to T
};

Неявное приведение из, скажем, Vector2 к Vector2 работает, как и ожидалось, но приведение из, скажем, Vector2 к Vector2 всегда вызывает (2, один для x и один для y) предупреждения, даже если использовался явный C-Style или static_cast. Я хочу сохранить предупреждения для неявных приведений, но не для явных приведений.

Я знаю, что могу взломать это, создав специальный метод типа T vector_cast (T2), который использует явное приведение для каждого элемента внутри, но я бы предпочел использовать C-Style и static_casts

Ответы [ 3 ]

1 голос
/ 07 октября 2009

Часть того, что вы хотите, не будет возможной, потому что она опирается на специальные знания, которые есть у компилятора о задействованных типах, и вы не можете научить компилятор этим вещам.

Предметы, которые вы показали, должны делать следующее:

Float a = 5.5;

Должен работать без жалоб.

std::string s = a;

Должен выдать некоторую ошибку компилятора, не обязательно такую ​​же, как при использовании плавающего POD, но все равно он откажется, так как ваш Float не имеет оператора const char *. (И если это произойдет, удалите его, чтобы вызвать эту ошибку.)

Int x = a;

Вы все равно должны получить предупреждение о возможной потере данных здесь, если у Float нет оператора "int ()". Если это так, удалите его, поэтому компилятор вынужден использовать «оператор float ()», вызывая предупреждение.

Int y = (int)a;

Должен работать без жалоб.

Int z = static_cast<int>a;

Это должно иметь тот же очевидный эффект, что и предыдущий. (Между ними есть технические различия, но в этом случае они не должны иметь значения.)

Float b = c;

Вы не показываете, что такое «с», поэтому я не могу сказать, что это будет делать.

1 голос
/ 07 октября 2009

Я тоже не думаю, что есть выход. Лучшее, чего я мог достичь, это чтобы строка, которую вы хотите создать, не компилировалась вообще.

class Int
{
public:
    int value;
    Int(int v);
};

class Float
{
public:
    float value;
    Float(float v);
    operator int() { return static_cast<int>(value); }
};

int main()
{
    Float a = 5.5;
    //Int x = a; //no warning, simply doesn't compile
    Int y = (int)a;
    Int z = static_cast<int>(a);
}

Изменить: относительно вашего вопроса о Vector2

Единственное, что нужно сделать, это отключить все неявные преобразования между различными типами Vector2. Для краткости вы можете указать vector_cast, чтобы разрешить явные преобразования:

template <class T, class S>
Vector2<T> vector_cast(const Vector2<S>& s)
{
    return Vector2<T>(static_cast<T>(s.x), static_cast<T>(s.y));
}

Другая вещь может заключаться в том, чтобы ввести некоторое шаблонное метапрограммирование, чтобы включить конструктор преобразования для безопасных преобразований.

Мне кажется, что в надстройке нет такого type_trait, поэтому я катал свой собственный.

Это несколько упрощено: цель должна быть, по крайней мере, такой же большой, как источник, и цель не должна быть интегральной, если источник - это число с плавающей запятой. Однако в нем не учитываются проблемы со знаком и вопрос о том, может ли тип с плавающей запятой представлять полный диапазон целочисленного типа (например, float не может точно хранить все 32-битные числа, а double может).

#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>

template <class S, class T>
struct is_safe_conversion:
    boost::integral_constant<
        bool,
        (sizeof(S) <= sizeof(T)) && !(boost::is_floating_point<S>::value && boost::is_integral<T>::value)
    >
{
};

template<typename T> class Vector2
{
public:
    T x, y;
    Vector2():x(0),y(0){}
    Vector2(T x, T y):x(x),y(y){}

    template <class U>
    Vector2(const Vector2<U>& other, typename boost::enable_if<is_safe_conversion<U, T> >::type* = 0):
        x(other.x), y(other.y) {}

};

template <class T, class S>
Vector2<T> vector_cast(const Vector2<S>& s)
{
    return Vector2<T>(static_cast<T>(s.x), static_cast<T>(s.y));
}

int main()
{
    Vector2<double> vd, vd2;
    Vector2<int> vi, vi2;
    Vector2<float> vf, vf2;

    vd = vd2;
    vd = vi;
    vd = vf;

    //vi = vd; //error
    vi = vector_cast<int>(vd);
    vi = vi2;
    //vi = vf; //error
    vi = vector_cast<int>(vf); //explicit

    //vf = vd; //error
    vf = vector_cast<float>(vd);

    //following compiles, but produces a warning (float cannot represent all integers) 
    //TODO: enhance is_safe_conversion!
    vf = vi; 
    vf = vf2;
}
0 голосов
/ 07 октября 2009

Я не думаю, что есть какой-либо способ создать собственное предупреждение компилятора для вашего приведения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...