问题
Consider the code:
#include <iostream>
template <class... Ts>
struct outer {
template <class... ITs>
struct inner {
static constexpr bool value = false;
};
template <class... ITs>
struct inner<Ts..., ITs...> {
static constexpr bool value = true;
};
};
int main() {
std::cout << outer<int, float, double>::inner<int, float, double, int>::value << std::endl;
}
The code compiles with clang++ but not with g++ where it produces an error:
temp3.cc:11:11: error: parameter pack argument ‘Ts ...’ must be at the end of the template argument list
struct inner<Ts..., ITs...> { ^
As I've already established here partial specialisation of the inner class should be legit.
Edit: For completeness it is worth adding that clang for the above code warns that he might have a problem with deducing ITs parameters yet doing it without any problems...
回答1:
This is a gcc bug. This is a perfectly valid partial specialization:
template <class... ITs>
struct inner<Ts..., ITs...> {
static constexpr bool value = true;
};
Deduced template parameter packs must be last, and ITs...
satisfies that. But Ts...
isn't a pack that needs to be deduced here, it's just a specific parameter pack.
Furthermore, gcc compiles several equivalent formulations:
template <class... Ts>
struct X {
template <class... Us>
static void foo(Ts..., Us...) { }
};
int main() {
X<int>::foo(1, 'c');
}
and:
template <class... Us>
struct A { };
template <class... Ts>
struct X {
template <class... Us>
static void foo(A<Ts..., Us...>) { }
};
int main() {
X<int>::foo(A<int, char>{});
}
These are equivalently well-formed to your original example.
回答2:
Riding on W.F.'s answer, still keeping the same main as in the original question:
#include <iostream>
template <class... Ts>
struct pack { };
template <class... Ts>
class outer {
template <class IT>
struct _inner {
static constexpr bool value = false;
};
template <class... ITs>
struct _inner<pack<Ts..., ITs...>> {
static constexpr bool value = true;
};
public:
template <class... ITs>
struct inner {
static constexpr bool value = _inner<pack<ITs...>>::value;
};
};
int main() {
std::cout << outer<int, float, double>::inner<int, float, double, int>::value
<< std::endl;
}
It still produces a warning in clang, because the specialized version of _inner couldn't deduce ITs... apart from the list of Ts..., ITs... (in struct _inner<pack<Ts..., ITs...>>
) -- however the code do not require ITs to be deduced separately from the list of Ts..., ITs..., so this should be ok.
In g++ it compiles without a warning.
Code: http://coliru.stacked-crooked.com/a/ae3b21dd847450b2
(For a solution without a warning also in clang: http://coliru.stacked-crooked.com/a/0c6c643c8ff5809e).
回答3:
Possible simple yet effective workaround inspired on Barry's answer:
#include <iostream>
template <class... Ts>
struct pack { };
template <class... Ts>
struct outer {
template <class IT>
struct inner {
static constexpr bool value = false;
};
template <class... ITs>
struct inner<pack<Ts..., ITs...>> {
static constexpr bool value = true;
};
};
int main() {
std::cout << outer<int, float, double>::inner<pack<int, float, double, int>>::value << std::endl;
}
(It still produces the warning in clang though)
来源:https://stackoverflow.com/questions/37767558/is-it-legal-to-partially-specialise-variadic-template-inner-class-with-args-from