Is it valid to have variations of the same template function that differ by the type of a non-type member?
template<typename T, unsigned int V>
void f(unsigned int& v) { v = V; }
template<typename T, bool B>
void f(bool& b) { b = B; }
The intent is that one be able to call
unsigned int meaningOfLife;
f<sometype, 42>(meaningOfLife);
bool areYouAlive;
f<sometype, true>(areYouAlive);
clang and gcc are silent but MSVC reports
warning C4305: 'specialization': truncation from 'int' to 'bool'
I'd like to avoid requiring specification of the constant type:
f<sometype, bool, true>
and want to ensure that the constant value and the destination value match.
---- mcve ----
#include <iostream>
template<unsigned int V>
void f(unsigned int& v) { v = V; }
template<bool B>
void f(bool& b) { b = B; }
int main()
{
unsigned int u { 0 };
bool b { false };
f<42>(u);
f<true>(b);
std::cout << u << b;
}
Rextester example: http://rextester.com/VIGNP16100
Warning(s):
source_file.cpp(14): warning C4305: 'specialization': truncation from 'int' to 'bool'
/LIBPATH:C:\boost_1_60_0\stage\lib
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23506 for x64
421
Short answer: The code is OK and MSVC emits a bogus warning.
MSVC and g++ both have bugs in non-type template argument matching but they do select the right one for your particular example.
Long answer: It is OK to have overloaded function templates with non-type template parameters.
However the matching of a template-argument to a template declaration does not work as might be expected (by me anyway). ALL matching templates are entered into overload resolution. It does not, at any stage, prefer an "exact match".
According to C++17 [temp.arg.nontype/]2, converted constant expressions are allowed. That means, for example:
42
matchesint
andunsigned int
.42u
matchesint
andunsigned int
.1u
matchesunsigned int
,int
andbool
.Note that a converted constant expression cannot contain a narrowing conversion, and
int
tobool
is narrowing unless the value is a constant expression of value0
or1
. So42
does not matchbool
. (Ref: C++17 [expr.const]/4).If we had the following setup:
then the correct behaviour is:
g<42>()
callsg<unsigned int>
.g<1>()
is ambiguous.g<1u>()
is ambiguous.MSVC 2017 and g++ 7,8 all incorrectly allow
g<42>
to matchg<bool>
, and reportg<42>
as ambiguous.MSVC emits the warning that you see whilst generating the invalid match; g++ gives no diagnostic at all. If we remove the
unsigned int
overload then g++ silently accepts the invalid code with no diagnostic.In your code there is a non-const lvalue reference parameter:
This makes a difference because overload resolution can make a selection based on the function argument. For the call:
then both overloads of
h
are entered into overload resolution, however thenh<unsigned int>
wins becauseh<bool>(m)
would be invalid.As discussed above, The call
h<42>(m);
wins at the first stage because this cannot matchh<bool>
; but in MSVC++ (and g++) it incorrectly allowsh<bool>
to go through at this stage, but prunes it later as for theh<1u>
case.