Принудительное сужение предупреждения о преобразовании

Рассмотрим следующий код, который иллюстрирует некоторые сужающие преобразования:

template <class T>
class wrapper 
{   
    template <class> friend class wrapper;
    public:
        constexpr wrapper(T value)
        : _data(value)
        {}
        template <class U>
        constexpr wrapper(wrapper<U> other)
        : _data(other._data) 
        {}
        wrapper& operator=(T value)
        {_data = value; return *this;}
        template <class U>
        wrapper& operator=(wrapper<U> other)
        {_data = other._data; return *this;}
    private:
        T _data;
};

int main(int argc, char* argv[]) 
{
    wrapper<unsigned char> wrapper1 = 5U;
    wrapper<unsigned char> wrapper2{5U};
    wrapper<unsigned char> wrapper3(5U);
    wrapper<unsigned int> wrapper4 = 5U;
    wrapper<unsigned int> wrapper5{5U};
    wrapper<unsigned int> wrapper6(5U);
    wrapper<unsigned char> wrapper7 = wrapper4;  // Narrowing
    wrapper<unsigned char> wrapper8{wrapper5};  // Narrowing
    wrapper<unsigned char> wrapper9(wrapper6);  // Narrowing
    wrapper7 = wrapper4;  // Narrowing
    wrapper8 = wrapper5;  // Narrowing
    wrapper9 = wrapper6;  // Narrowing
    return 0;
}

Как поменять тело wrapper члены, так что это вызывает предупреждение компилятора для сужения преобразования? Моя цель - дать пользователю понять, что в его коде что-то не так.

3 ответа

Решение

Вы можете вызвать предупреждение о сужающемся преобразовании с единым синтаксисом инициализации:

class wrapper 
{   
    template <class> friend class wrapper;
    public:
        constexpr wrapper(T value)
        : _data{value}
        {}
        template <class U>
        constexpr wrapper(wrapper<U> other)
        : _data{other._data} // note the curly brackets here
        {}
        wrapper& operator=(T value)
        {_data = value; return *this;}
        template <class U>
        wrapper& operator=(wrapper<U> other)
        {_data = {other._data}; return *this;} // and here
    private:
        T _data;
};

с

wrapper<unsigned int> wrapper1 = 5U;
wrapper<unsigned char> wrapper2 = wrapper1;  // Narrowing
wrapper<unsigned char> wrapper3(wrapper1);  // Narrowing
wrapper<unsigned char> wrapper4{wrapper1};  // Narrowing
wrapper2 = wrapper1;  // Narrowing

любая из четырех последних строк выдаст предупреждение о сужающемся преобразовании в g++ и ошибки компиляции из-за сужающихся преобразований в clang.

Чтобы остановить компиляцию при сужающемся вызове, вы можете использовать SFINAE на

template <class U>
constexpr wrapper(wrapper<U> other)
: _data(other._data) 
{}

И изменить его на

template <class U, typename std::enable_if<sizeof(T) >= sizeof(U)>::type* = nullptr>
constexpr wrapper(wrapper<U> other)
: _data(other._data) 
{}

Живой пример

Это остановит компиляцию, если размер базового типа, из которого вы копируете, больше, чем базовый тип объекта, который вы инициализируете.

Я бы порекомендовал другой способ. Вам не нужно гадить с вашим кодом. Когда вы компилируете, используя g++, добавьте флаг -Wconversion.

Другие вопросы по тегам