The following code, which attempts to specialize class template 'special', based on the return type of member function pointer types, results in a compile error with VC9:
template<class F> struct special {};
template<class C> struct special<void(C::*)()> {};
template<class R, class C> struct special<R(C::*)()> {};
struct s {};
int main()
{
special<void(s::*)()> instance;
return 0;
}
error C2752: 'special' : more than one partial specialization matches the template argument list
The same code is accepted by GCC-4.3.4, as shown by: http://ideone.com/ekWGg
Is this a bug in VC9 and if so, has this bug persisted into VC10?
I have however come up with a horrendously intrusive workaround (for this specific use case, at least. More general solutions welcome):
#include <boost/function_types/result_type.hpp>
#include <boost/type_traits/is_same.hpp>
template<typename F, typename R>
struct is_result_same :
boost::is_same<
typename boost::function_types::result_type<F>::type,
R
>
{};
template<class F, bool = is_result_same<F, void>::value>
struct special {};
template<class R, class C> struct special<R(C::*)(), true> {};
template<class R, class C> struct special<R(C::*)(), false> {};
This is a bug.
According to 14.5.4.2, the partial ordering of these two class template specializations are the same as the partial ordering of these imaginary function templates:
According to 14.5.5.2, the partial ordering of these two function templates is determined by substituting invented types for each type template parameter in the argument list of one and attempting template argument deduction using that argument list in the other function template.
The details of template argument deduction are in 14.8.2. Among the valid deductions are from
template_name<dependent_type>
anddependent_type1 (dependent_type2::*)(arg_list)
. So thef4(arg3())
deduction succeeds, deducingf4<void,ty5>(arg3());
. Thef3(arg4())
deduction can obviously never succeed, sincevoid
andty6
do not unify.Therefore function template 3 is more specialized than function template 4. And class template specialization 1 is more specialized than class template specialization 2. So although
special<void(s::*)()>
matches both specializations, it unambiguously instantiates specialization 1.