Vincent
Vincent

Reputation: 60381

Force narrowing conversion warning

Consider the following code that illustrates some narrowing conversions:

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;
}

How to change the body of the wrapper members, so that it triggers the compiler warning for narrowing conversion? My goal is to make the user aware that something is potentially wrong with their code.

Upvotes: 3

Views: 1327

Answers (3)

jaggedSpire
jaggedSpire

Reputation: 4513

You can trigger a narrowing conversion warning with uniform initialization syntax:

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;
};

with

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

any of the four last lines will produce a narrowing conversion warning in g++, and compilation errors from the narrowing conversions in clang.

Upvotes: 5

NathanOliver
NathanOliver

Reputation: 180660

To stop compilation in a narrowing call you can use SFINAE on

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

And change it to

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

Live Example

This will stop compilation if the size of the underlying type you are copying from is greater than the underlying type of the object you are initializing.

Upvotes: 3

Donghui Zhang
Donghui Zhang

Reputation: 1133

I'd recommend a different way. You don't need to muck with your code. When you compile using g++, add the -Wconversion flag.

Upvotes: 0

Related Questions