C++不指定参数约束函数返回类型?编译错误原因及思路验证
函数返回类型约束的模板特化匹配问题
问题描述
尝试在不要求指定函数参数的前提下,约束客户端提供函数的返回类型,编写了如下代码:
#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; }
static_assert可正常通过,但使用foo函数实例化g()时出现编译错误,错误信息如下:
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
编译器似乎选择了ReturnTypeWrapper的主模板而非函数指针特化版本,请问原因是什么?实现思路是否正确?(使用编译器为gcc 11.3.0)
原因分析
核心问题在于函数名到函数指针的隐式转换:
- 当
static_assert中使用decltype(foo)时,foo是函数类型void(double, int),能匹配到ReturnTypeWrapper<R(Args...)>的特化,所以type被正确推导为void。 - 但在模板参数
auto c中传入foo时,函数名会被隐式转换成函数指针类型void(*)(double, int),而你的ReturnTypeWrapper没有针对函数指针的特化版本,因此编译器只能匹配主模板,主模板中没有定义type成员,导致编译错误。
你的实现思路方向是对的,但遗漏了函数指针类型的处理场景。
解决方案
可以通过两种方式修复:
方式1:添加函数指针的特化版本
给ReturnTypeWrapper增加针对函数指针的特化,将指针类型转换为对应的函数类型后复用已有逻辑:
template <typename R, typename... Args> struct ReturnTypeWrapper<R(*)(Args...)> { using type = typename ReturnTypeWrapper<R(Args...)>::type; };
方式2:在concept中移除指针修饰
修改ReturnVoid概念,先用std::remove_pointer_t去掉可能的函数指针修饰,再传入ReturnTypeWrapper:
template <typename T> concept ReturnVoid = std::is_same_v<void, typename ReturnTypeWrapper<std::remove_pointer_t<T>>::type>;
修改后,g<foo>()就能正常编译,因为无论是函数类型还是函数指针类型,都能正确匹配到特化版本并推导返回类型。
内容的提问来源于stack exchange,提问作者biubiuty
相关产品推荐
相关产品推荐

