'Type conflicts in non-type template argument deduction
#include<type_traits>
template <typename T, T>
struct A { };
template <typename T, T t>
void f(A<T, t>) {
}
int main() {
f(A<const int, 0>{});
}
https://godbolt.org/z/n6bcj5rjM
The program is accepted by GCC and ICC in C++14 and C++17 mode, as well as Clang in C++14 mode but rejected by Clang in C++17 mode and MSVC in either mode.
Rejections are with diagnostics like this:
<source>:12:5: error: no matching function for call to 'f'
f(A<const int, 0>{});
^
<source>:7:6: note: candidate template ignored: deduced conflicting types for parameter 'T' ('const int' vs. 'int')
void f(A<T, t>) {
^
Also consider the following variation:
const int i = 0;
int main() {
f(A<const int&, i>{});
}
https://godbolt.org/z/oa3xfv4jx
The same compilers still accept/reject and those rejecting now complain about a type mismatch between const int& and int.
Which compiler(s) are correct?
I expect the answer to depend on whether it is before or after C++17, since C++17 introduced a (breaking) change in non-type template argument deduction, see [diff.cpp14.temp].
In particular for C++17 and later I am wondering whether deduction of T from the argument for t is supposed to deduce the type of the template parameter, which would be int in the first variant (because top-level const is ignored) and const int& in the second variant, or whether the usual expression adjustments are supposed to be applied as in deduction with auto ... = t;, in which case the second variant should deduce T to int.
Is it then really intended that template argument deduction fails if these types mismatch with the explicitly provided type for T?
Solution 1:[1]
The explanation given below is for the 1st snippet(also shown below):
//----------------------v--->i named the parameter for explanation purposes
template <typename T, T p>
struct A { };
template <typename T, T t>
void f(A<T, t>) {
}
int main() {
f(A<const int, 0>{});
}
Step 1
Here we consider what happens for the expression A<const int, 0>{}.
From temp.param#5:
The top-level cv-qualifiers on the template-parameter are ignored when determining its type.
On the first glance it seems that when applied to the given snippet, the non-type template parameter named p should be int instead of const int.
But note the emphasis on the highlighted part in the above quoted statement. In particular, my interpretation of this(the phrase "when determining") is that the top-level cv-qualifiers are dropped when deducing the template parameter and so not when the template arguments are explicitly specified.
And since in my given snippet, we are explicitly specifying the template argument, there is no template argument deduction(TAD) here. Thus, the dropping of the top-level cv-qualifiers due to the above quoted temp.param#5 does not happen. Meaning the type of p is const int instead of int.
Note that i may be wrong in interpreting [temp.param#5] above.
But before further analysis, from temp.param#6:
A non-type non-reference template-parameter is a prvalue. It shall not be assigned to or in any other way have its value changed.
Moreover, from expr#6:
If a prvalue initially has the type “cv T”, where T is a cv-unqualified non-class, non-array type, the type of the expression is adjusted to T prior to any further analysis.
When applying temp.param#6 and expr#6 to the given snippet, it means that p(a prvalue) will finally be of type int instead of const int.
Thus, the non-type template parameter p is of type int.
Step 2
Next, we consider what happens for the call expression f(A<const int, 0>{}).
Now currently we have a prvalue A<const int, 0> whose first type parameter T is const int while the second non-type template parameter p is of type int. Thus, for the function template f<> the first template type parameter T is deduced to be const int while the type of non-type template parameter t is deduced to be int. Thus there is a conflict between the type of T and hence the mentioned error saying:
<source>:7:6: note: candidate template ignored: deduced conflicting types for parameter 'T' ('const int' vs. 'int')
void f(A<T, t>) {
^
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Anoop Rana |
