I was trying to constrain the return type of client-provided functions without asking to specify the function parameters:
#include <type_traits>
template <typename T>
struct ReturnTypeWrapper {};
template <typename R, typename... Args>
struct ReturnTypeWrapper<R(Args...)> {
using type = R;
};
template <typename T>
concept ReturnVoid = std::is_same_v<void, typename ReturnTypeWrapper<T>::type>;
template <auto c>
requires(ReturnVoid<decltype(c)>)
void g() {}
void foo(double, int){};
int main() {
static_assert(std::is_same_v<void, typename ReturnTypeWrapper<decltype(foo)>::type>);
g<foo>();
return 0;
}
While there is no problem with the static_assert
, when it comes to instantiating g
with the foo
function, I have the following compile error:
xxx/Concept.cpp: In function ‘int main()’:
xxx/Concept.cpp:24:11: error: no matching function for call to ‘g<foo>()’
24 | g<foo>();
| ~~~~~~^~
xxx/Concept.cpp:17:6: note: candidate: ‘template<auto c> requires ReturnVoid<decltype(c)> void g()’
17 | void g() {}
| ^
xxx/Concept.cpp:17:6: note: template argument deduction/substitution failed:
xxx/Concept.cpp:17:6: note: constraints not satisfied
xxx/Concept.cpp: In substitution of ‘template<auto c> requires ReturnVoid<decltype(c)> void g() [with auto c = foo]’:
xxx/Concept.cpp:24:11: required from here
xxx/Concept.cpp:13:9: required for the satisfaction of ‘ReturnVoid<decltype (c)>’ [with c = &foo()]
xxx/Concept.cpp:13:27: error: no type named ‘type’ in ‘struct ReturnTypeWrapper<void (*)(double, int)>’
13 | concept ReturnVoid = std::is_same_v<void, typename ReturnTypeWrapper<T>::type>;
| ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
make[3]: *** [cpp/Template/CMakeFiles/template-concept.dir/build.make:76: cpp/Template/CMakeFiles/template-concept.dir/Concept.cpp.o] Error 1
make[2]: *** [CMakeFiles/Makefile2:5580: cpp/Template/CMakeFiles/template-concept.dir/all] Error 2
make[1]: *** [CMakeFiles/Makefile2:5587: cpp/Template/CMakeFiles/template-concept.dir/rule] Error 2
make: *** [Makefile:1941: template-concept] Error 2
It appears that only the primary template is chosen rather than the specialized one for function pointers. Why is it so? Am I on the right track to constrain function return type without specifying parameters?
PS: Compiler is gcc 11.3.0.
template <auto c>
requires (ReturnVoid<decltype(c)>)
void g() {}
The issue here is that when you call g<foo>()
, what you get for c
isn't void(double, int)
- because you can't have values of function style. You get a function-to-pointer conversion, so c
is actually a void(*)(double, int)
. This doesn't match your specialization for ReturnTypeWrapper
which only handled the pattern R(Args...)
.