Reputation: 60381
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
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
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)
{}
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
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