std::type_identity can be used to provide non-deducible context. So, I wondered if it will work on a deduced variadic list. But different compilers give different results.
https://godbolt.org/z/4cfqbxdeo
#include <type_traits>
struct in_between{};
template <typename... T>
struct args_tag
{
using type = std::common_type_t<T...>;
};
template <typename... T>
void bar(args_tag<T...>, std::type_identity_t<T>..., int, std::type_identity_t<T>...) {}
template <typename... T>
void bar(args_tag<T...>, std::type_identity_t<T>..., in_between, std::type_identity_t<T>...) {}
// example
int main() {
bar(args_tag<int, int>{}, 4, 8, 15, 16, 23);
bar(args_tag<int, int>{}, 4, 8, in_between{}, 16, 23);
}
The first one compiles with gcc and msvc.
bar(args_tag<int, int>{}, 4, 8, 15, 16, 23);
The second one compiles only with msvc.
bar(args_tag<int, int>{}, 4, 8, in_between{}, 16, 23);
What should be the behavior according to the standard?
The program is well-formed for both the calls for the following reason(s). In the given example, deduction is performed only using the first function parameter
args_tag<T...>. The remaining two parameter packs won't be involved in deduction since they are in non-deduced context. After the deduction is performed using the first parameterargs_tag<T...>, the deducedTis substituted into the remaining two parameter packs as per temp.deduc.general#3. This means that for the callbar(args_tag<int, int>{}, 4, 8, 15, 16, 23),Tgets deduced as{int, int}. Now for each of the args in this list{int, int}, we get a correspondingstd::type_identityparameter(4 in total because there are twostd::type_identityparameter andThas twoints) as shown in the below declaration. Essentially, the declaration becomes:Now as we can see, this is viable for the call
bar(args_tag<int, int>{}, 4, 8, 15, 16, 23)and so should succeed.This can be seen from temp.deduct.general#3:
(emphasis mine)
That is, gcc and clang are wrong in giving a diagnostic. See also the contrived example at the end to make this more clear.
This also explains why a call such as
bar(args_tag<int, int, double>{}, 4, 8, 15, 16, 23, 34,34);passes while the callbar(args_tag<int, int, double>{}, 4, 8, 15, 16, 23, 34)fails in all compilers.Contrived Example
To make the above explanation more clear, below is a contrived example showing the logic/reasoning. Note that the example illustrates what should be happening in your program(both calls should compile).
Demo
GCC rejects valid program involving parameter packs with in between class type
Clang rejects valid program involving parameter packs with in between type