I encountered a case where I cannot understand the implicit conversion behavior in c . The code is the following:
template <bool b, int i, unsigned... us>
void foo() {}
template <int i, unsigned... us>
void foo() {return foo<false, i, us...>();}
int main()
{
foo<true, -1, 0ul, 1ul, 2ul>(); // compiles with clang and gcc > 8 (gcc<=8 gives ambiguous function call)
foo<true, 1, 0ul, 1ul, 2ul>(); // ambiguous function call error
foo<-1, 0ul, 1ul, 3ul>(); // compiles with clang and gcc > 8 (gcc<=8 gives ambiguous function call)
foo< 1, 0ul, 1ul, 3ul>(); // ambiguous function call error
}
For line 1 and 2 in main()
, the compiler seems to implicitly cast int
to unsigned
only for positive integers (this is understandable but is there a rule for this?)
For line 3 and 4 in main()
, the compiler seems to implicitly cast int
to bool
only for positive integers. I cannot understand why.
Background: Ideally, I want to get a similar construct to work which effectly leads to a default value for the bool
template parameter. But because of the parameter pack and c implicit builtin conversions, this seems to be difficult.
CodePudding user response:
A non-type template argument is required to be a converted constant expression of the template parameter's type.
A converted constant expression specifically does not allow for narrowing conversions, which in the case of constant expression evaluation between integral types means that the conversion is not allowed if it would change the numeric value.
This is specific to the converted constant expression requirement. If these were function parameter and argument, then implicit conversion of -1
to unsigned
would be allowed and it also has a well-defined result, although obviously not with the same numeric value of -1
.
Therefore in foo<true, -1, 0ul, 1ul, 2ul>();
the template which requires unsigned
in that position for -1
is indeed not viable as an overload candidate.
Boolean conversions, meaning conversions from other scalar types to bool
are not listed in the list of conversions allowed for a converted constant expression at all (see [expr.const]/10), which as far as I can tell should make the overload with bool
in the first position non-viable for both foo<-1, 0ul, 1ul, 3ul>();
and foo< 1, 0ul, 1ul, 3ul>();
. I am not sure why the compilers consider the latter ambiguous anyway. According to the resolution of CWG issue 1407 as not-a-defect, the conversion from int
template argument to bool
template parameter is indeed not allowed, but compilers are behaving non-conforming and seem to treat it like an integral type with range 0
/1
.
I don't know what problem GCC <8 has. I guess it was just a bug that it considered the overload ambiguous.