Why code below compile in Visual Studio 2019 and does not with gcc 8.3?
#include<array>
template<typename T> class myClass {
public:
template<unsigned int N> myClass(const std::array<T, N>& elems) { /* do something */ }
};
int main() {
std::array<int, 10> A;
myClass<int> Tabular(A);
}
This is a snippet I extract from my student's project because it looks weird to me. I try this with gcc 8.3 and as I suspect the compiler complains that template argument deduction is failed. So I told my student that this does not work. But he argued that this do compile in VS without warning, I checked and he was right.
Because I'm very far from calling myself an expert, I cannot explain to myself and my student whether/why/what is wrong.
Both Gcc and MSVC are correct. The type of the 2nd template parameter of std::array
is defined as std::size_t
, and how std::size_t
is defined depends on implementation.
typedef /*implementation-defined*/ size_t;
std::size_t
is the unsigned integer type of the result of the sizeof operatoras well as the sizeof... operator and the alignof operator (since C++11)
.The bit width of
std::size_t
is not less than 16. (since C++11)
Then when std::size_t
is defined as the same as unsigned int
the code compiles fine, otherwise it would fail; type mismatch causes the non-type template argument deduction fails.
Change unsigned int
to std::size_t
then the code is guaranteed to be compiled well with any decent compilers. e.g.
template<typename T> class myClass {
public:
template<std::size_t N> myClass(const std::array<T, N>& elems) { /* do something */ }
};
And about why type mismatch causes the non-type template argument deduction fails.
(emphasis mine)
If a non-type template parameter is used in the parameter list, and the corresponding template argument is deduced, the type of the deduced template argument ( as specified in its enclosing template parameter list, meaning references are preserved) must match the type of the non-type template parameter exactly, except that cv-qualifiers are dropped, and except where the template argument is deduced from an array bound—in that case any integral type is allowed, even bool though it would always become true:
template<int i> class A { }; template<short s> void f(A<s>); // the type of the non-type template param is short void k1() { A<1> a; // the type of the non-type template param of a is int f(a); // P = A<(short)s>, A = A<(int)1> // error: deduced non-type template argument does not have the same // type as its corresponding template argument f<1>(a); // OK: the template argument is not deduced, // this calls f<(short)1>(A<(short)1>) } template<int&> struct X; template<int& R> void k2(X<R>&); int n; void g(X<n> &x) { k2(x); // P = X<R>, A = X<n> // parameter type is int& // argument type is int& in struct X's template declaration // OK (with CWG 2091): deduces R to refer to n }
Whenever you have template errors like this, the best thing you can do is try different compilers, and see what errors they will give you. CompilerExplorer is a really good site for this
GCC says:
<source>:4:26: note: candidate: 'template<unsigned int N> myClass<T>::myClass(const std::array<T, N>&) [with unsigned int N = N; T = int]'
4 | template<unsigned int N> myClass(const std::array<T, N>& elems) { /* do something */ }
| ^~~~~~~
<source>:4:26: note: template argument deduction/substitution failed:
<source>:9:27: note: mismatched types 'unsigned int' and 'long unsigned int'
9 | myClass<int> Tabular(A);
|
Clang says:
<source>:4:26: note: candidate template ignored: substitution failure: deduced non-type template argument does not have the same type as the corresponding template parameter ('unsigned long' vs 'unsigned int')
template<unsigned int N> myClass(const std::array<T, N>& elems) { /* do something */ }
As you can see, both GCC and Clang failed to subsitute long unsigned int
(std::size_t
) with a unsigned int
.
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