The following C++ code compiles and runs correctly for GNU g++, LLVM and every other C++ compiler I threw at it except for Microsoft VC6 and VC7:
template<typename A, typename B> int HasVoidReturnType(A(*)(B)) { return 0; }
template<typename B> int HasVoidReturnType(void(*)(B)) { return 1; }
void f(double) {}
int foo() { return HasVoidReturnType(f); }
For VC6 and VC7, it fails to compile and gives the error:
f.cpp(4) : error C2667: 'HasVoidReturnType' : none of 2 overloads have a best conversion
f.cpp(2): could be 'int HasVoidReturnType(void (__cdecl *)(B))'
f.cpp(1): or 'int HasVoidReturnType(A (__cdecl *)(B))'
while trying to match the argument list '(overloaded-function)'
f.cpp(4) : error C2668: 'HasVoidReturnType' : ambiguous call to overloaded function
f.cpp(2): could be 'int HasVoidReturnType(void (__cdecl *)(B))'
f.cpp(1): or 'int HasVoidReturnType(A (__cdecl *)(B))'
while trying to match the argument list '(overloaded-function)'
Rather than arguing the merits of what compiler is right, how can I determine from a template function whether a function has a void return type using VC6 and VC7?
-
Instead of creating two templates, have you tried just using the first one and using template specialization to define the second?
-
FYI this is compilable on C++ 2008 Express edition from Microsoft. (I would have liked to help but can't reproduce the problem on my compiler)
-
Try this on for size
template<typename FuncPtrType> struct DecomposeFuncPtr; template<typename ReturnType, typename ArgType> struct DecomposeFuncPtr<ReturnType(*)(ArgType)> { typedef ReturnType return_type; }; template<typename T> struct is_void { enum { value = 0 }; }; template<> struct is_void<void> { enum { value = 1 }; }; template<typename T> int HasVoidReturnType(T dontcare) { return is_void< typename DecomposeFuncPtr<T>::return_type >::value; }
it should avoid the overloading that is confusing VC6/7.
Hrmm. Sorry I couldn't test it with VC6/7. I see to recall running into issues using function pointers with templates before in VC though. Since we know the A, B works for the function in your original, I wonder if something like:
template<typename T> struct is_void { enum { value = 0 }; }; template<> struct is_void<void> { enum { value = 1 }; }; template<typename A, typename B> int HasVoidReturnType(A(*)(B)) { return is_void<A>::value; }
would work.
Johannes Schaub - litb : Try templatechar (& HasVoidReturnType(A(*)(B)) )[is_void::value + 1]; then sizeof(HasVoidReturnType(fun)) can tell you either 1 or 2 depending on whether it returns void. maybe that works with vc6 too? it would work at compile time :) Greg Rogers : The last way is how I would have written it in the first place - but probably because I don't grok overload resolution compared to template specialization. -
As far as VC++ 6 is concerned, you are screwed, as it doesn't support partial template specialisation, which is what you need to solve this problem.
Andrew Medico : VC++ 6 can barely be called a "C++ compiler".ChrisInEdmonton : VC++ 7.0 also sucks. VC++ 7.1, on the other hand, is a pretty decent C++ compiler. The version numbering system here is unfortunate.