Consider the following code:
template<int N>
class Vector
{
};
#include <array>
template<int N>
void doWork(const Vector<N>&, const std::array<int,N>&)
{
}
int main()
{
std::array<int,3> arr;
Vector<3> vec;
doWork(vec,arr);
}
Here Vector
represents a class which is defined in a third-party library, and std::array
is known to take its element count as std::size_t
.
I've tried compiling this with clang-3.6 and g++-5.1. Clang worked without any complaint, while g++ gives the following error:
test.cpp: In function ‘int main()’:
test.cpp:17:19: error: no matching function for call to ‘doWork(Vector<3>&, std::array<int, 3ul>&)’
doWork(vec,arr);
^
test.cpp:9:6: note: candidate: template<int N> void doWork(const Vector<N>&, const std::array<int, N>&)
void doWork(const Vector<N>&, const std::array<int,N>&)
^
test.cpp:9:6: note: template argument deduction/substitution failed:
test.cpp:17:19: note: mismatched types ‘int’ and ‘long unsigned int’
doWork(vec,arr);
^
test.cpp:17:19: note: ‘std::array<int, 3ul>’ is not derived from ‘const std::array<int, N>’
I can work around this by doing a cast of N
to std::size_t
in second parameter of doWork()
or calling doWork<3>()
, but this wouldn't educate me.
So I rather ask first: which compiler is right here? Am I really doing something wrong in the code (so clang is too permissive), or is it indeed valid C++ (so that g++ has a bug)?
Template argument deduction is used in declarations of functions, when deducing the meaning of the auto specifier in the function's return type, from the return statement.
A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
When you call a function template, the compiler tries to deduce the template type. Most of the time it can do that successfully, but every once in a while you may want to help the compiler deduce the right type — either because it cannot deduce the type at all, or perhaps because it would deduce the wrong type.
Class Template Argument Deduction (CTAD) is a C++17 Core Language feature that reduces code verbosity. C++17's Standard Library also supports CTAD, so after upgrading your toolset, you can take advantage of this new feature when using STL types like std::pair and std::vector.
I believe gcc is correct here, if we go to the draft C++11 standard section 14.8.2.5
[temp.deduct.type] it says:
If, in the declaration of a function template with a non-type template-parameter, the non-type templateparameter is used in an expression in the function parameter-list and, if the corresponding template-argument is deduced, the template-argument type shall match the type of the template-parameter exactly, except that a template-argument deduced from an array bound may be of any integral type.144 [ Example:
template<int i> class A { /* ... */ }; template<short s> void f(A<s>); void k1() { A<1> a; f(a); // error: deduction fails for conversion from int to short f<1>(a); // OK }
[...]
and we can see if we change your code to this:
doWork<3>(vec,arr);
gcc does not issue an error and neither does clang.
If we try this example:
template<int N>
void doWorkB( std::array<int,N>&)
{
}
//...
doWorkB(arr);
clang now produces an error (see it live):
note: candidate template ignored: substitution failure : deduced non-type template argument does not have the same type as the its corresponding template parameter ('unsigned long' vs 'int')
void doWorkB( std::array<int,N>&)
^
Your original case also breaks in clang if we swap the parameter order:
void doWork( const std::array<int,N>&, const Vector<N>& )
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