narrowing warnings while initializing an array

44 minutes ago 1
ARTICLE AD BOX

I have a class that I'm using across a somewhat large project, and it has a set of functions which need to take, as one argument, an array-like argument full of integral values. The size of the array has to match a template parameter in the class. Here's a simplified analog:

template<std::size_t N> struct mystruct { void func(const std::array<std::size_t,N>&) { // code } }; int main() { mystruct<2> ms; ms.func({1,2}); return 0; }

This compiles without issue. However, if I do something like this:

int n = 3; ms.func({n,n});

I get a warning

warning: narrowing conversion of ‘n’ from ‘int’ to ‘long unsigned int’ [-Wnarrowing]

Fair enough. I think this warning should be taken seriously, and I do not want to use the -Wnarrowing option globally. Does anyone know if this warning can be turned off for just one function?

If I try this, I avoid that warning:

void func(const std::array<int,N>&) { // code with range check }

But then this code

mystruct<2> ms; std::vector<double> v; ms.func({v.size(),v.size()});

triggers this warning

warning: narrowing conversion of ‘v.std::vector<double>::size()’ from ‘std::vector<double>::size_type’ {aka ‘long unsigned int’} to ‘int’ [-Wnarrowing]

As it should. size_t and int have ranges that don't fully overlap. So, how about an overload for each?

template<std::size_t N> struct mystruct { void func(const std::array<std::size_t,N>&) { // code } void func(const std::array<int,N>&) { // code with range check } };

This is worse, since int and size_t can be converted, and we now get an ambiguity error:

error: call of overloaded ‘func(<brace-enclosed initializer list>)’ is ambiguous

What about initializer_lists?

template<typename T> void func(const std::initializer_list<T>&) { // check that the list's size matches N // code with range check }

That actually works in the above examples, but fails in the mixed case

mystruct<2> ms; std::vector<double> v; ms.func({3,v.size()});

With the error

error: no matching function for call to ‘mystruct<2>::func(<brace-enclosed initializer list>)’ test.cpp:18:29: note: template argument deduction/substitution failed: test.cpp:33:12: note: deduced conflicting types for parameter ‘_Tp’ (‘int’ and ‘long unsigned int’)

Here's another idea I tried:

template<typename... Args> void func(const std::tuple<Args...>&) { // check that the tuple's size matches N // code with range check }

But that produces

error: cannot convert ‘<brace-enclosed initializer list>’ to ‘const std::tuple<>&’

Any ideas? This class is part of a larger project, and these are all common use cases. One solution would be to do a range check at each point the function is called, and then static_cast the values to the right times. But, that would get cumbersome really fast.

Read Entire Article