This is a follow-up of another question. It refers to the same problem (I hope) but uses an entirely different example to illustrate it. The reason is that in the previous example only experimental GCC 4.9 failed with a compiler error. In this example, also Clang and GCC 4.8.1 fail in different ways: Clang produces an unexpected result and GCC 4.8.1 reports a different error message.
Answers to the previous question say more or less that the code is valid and the problem lies with the experimental version of GCC. But this result makes me a bit more sceptical. I have been troubled for months with problems that I suspect are related (or the same), and this is the first time I have a small concrete example to illustrate.
So, here is some code. First, some generic code that applies SFINAE to an arbitrary test as specified by a variadic template alias metafunction F
:
#include <iostream>
using namespace std;
using _true = integral_constant <bool, true>;
using _false = integral_constant <bool, false>;
template <typename T> using pass = _true;
template <template <typename...> class F>
struct test
{
template <typename... A> static _false _(...);
template <typename... A> static pass <F <A...> > _(int);
};
template <template <typename...> class F, typename... A>
using sfinae = decltype(test <F>::template _<A...>(0));
Second, a specific test, checking if a given class has defined a type named type
:
template <typename T> using type_of = typename T::type;
template <typename T> using has_type = sfinae <type_of, T>;
Finally, an example:
struct A { using type = double; };
int main()
{
cout << has_type <int>() << ", ";
cout << has_type <A>() << endl;
}
The expected result would be 0, 1
. Clang says 0, 0
. GCC 4.8.1 says
tst.cpp: In substitution of ‘template<class T> using type_of = typename T::type [with T = A ...]’:
tst.cpp:15:51: required from ‘struct test<type_of>’
tst.cpp:19:67: required by substitution of ‘template<template<class ...> class F, class ... A> using sfinae = decltype (test:: _<A ...>(0)) [with F = type_of; A = {T}]’
tst.cpp:24:58: required from here
tst.cpp:23:56: error: ‘A ...’ is not a class, struct, or union type
template <typename T> using type_of = typename T::type;
^
and GCC 4.9 says
tst.cpp:19:67: required by substitution of ‘template<template<class ...> class F, class ... A> using sfinae = decltype (test:: _<A ...>(0)) [with F = type_of; A = {T}]’
tst.cpp:24:58: required from here
tst.cpp:15:51: error: pack expansion argument for non-pack parameter ‘T’ of alias template ‘template<class T> using type_of = typename T::type’
template <typename... A> static pass <F <A...> > _(int);
^
(line numbers may vary). So, everything fails, in different ways.
Now, here is a workaround. Metafunction car
picks the first type fom a given pack, and then the test is redefined as type_of2
, now being variadic:
template <typename... T> struct car_t;
template <typename... T> using car = type_of <car_t <T...> >;
template <typename T, typename... Tn>
struct car_t <T, Tn...> { using type = T; };
template <typename... T> using type_of2 = typename car <T...>::type;
template <typename T> using has_type2 = sfinae <type_of2, T>;
int main()
{
cout << has_type2 <int>() << ", ";
cout << has_type2 <A>() << endl;
}
Now all three compilers say 0, 1
as expected. It is interesting that for any version of GCC we have to remove has_type
(even if we don't use it) and leave only has_type2
; otherwise we have similar error.
To wrap up: I see the problem with one template expecting a variadic template-parameter of the form
template <typename...> class F
where we actually give as input a non-variadic template alias of the form
template <typename T> using alias = // ... anything including T or not
and finally invoke F
as if it was variadic:
F <A...>
Opinions so far say this is valid, but now it seems three compilers disagree. So the question is again: is it valid?
To me it matters because I have dozens of files of existing code based on the assumption that this is valid, and now I need a redesign anyway (since there are practical problems with these compilers) but the exact redesign will depend on the answer.
0, 0
. (Concerning the first test). – Cristalcristate