I usually use std::size_t
where integral constants are needed in template parameters. What I notice though, is that the type system doesn't protect me from users that are happy to pass negative numbers as arguments to those parameters.
For example the following compiles giving disastrous results :
#include <iostream>
template<std::size_t I>
struct E1
{
static void apply()
{
std::cout << I << std::endl;
}
};
template<typename T>
constexpr T a = T { -1 };
template<std::size_t... Is>
void E2()
{
for (auto&& i : {Is...}) std::cout << i << " ";
std::cout << std::endl;
}
int main()
{
E1<-1>::apply();
E2<-1, -2, -3>();
//std::cout << a<std::size_t>;
}
Demo
Interestingly this is not allowed for variable templates (uncommenting the last line in main
causes a compilation error).
Is there any solution/workaround for the struct
and function
case ?
This is actually a GCC bug, as there should be a diagnostic(warning or error) for narrowing here. The bug report is Narrowing conversions not caught in non-type template parameters and it has the following example:
template< char> void f () {}
template<unsigned int> void g () {}
template<unsigned int> struct A {};
int main () {
f<1024> (); // ill-formed, `char { 1024 }` is a narrowing conversion,
// see [dcl.init.list]p7
g<-123> (); // ill-formed, `unsigned int { -123 }` is a narrowing
// conversion, see [dcl.init.list]p7
A<-123> a; // ill-formed, see previous comment
}
and it points out the relevant quotes from the C++ standard:
[temp.arg.nontype]p1;
A template-argument for a non-type, non-template template parameeter shall be one of:
for a non-type template-aprameter of integral or enumeration type, a converted constant expression (5.19) of the type of the template-parameter
<snip />
[expr.const]p3;
A converted constant expression of type
T
is an expression, implicitly converted to a prvalue of typeT
, where the converted expression is a core constant expression and the implicit conversion sequence contains only user-defined conversions, lvalue-to-rvalue conversions (4.1), integral promotions (4.5), and integral conversions (4.7) other than narrowing conversions (8.5.4).
[ Note:
gcc
accepts testcase.cpp, whileclang
(correctly) issues the relevant diagnostics. ]
as sehe noted clang correctly produces a diagnostic for this with the following error:
error: non-type template argument evaluates to -1, which cannot be narrowed to type 'std::size_t' (aka 'unsigned long') [-Wc++11-narrowing]
E1<-1>::apply();
^
error: no matching function for call to 'E2'
E2<-1, -2, -3>();
^~~~~~~~~~~~~~
As a work-around you can use -Wsign-conversion -Werror
(both documented here) to warn about this case and turn it into an error (see it live). I originally thought this would be caught by -Wconversion
but -Wsign-conversion
is turned off by default in C++.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With