C++定义含int及可变参数的runnable概念遇编译错误的技术问询
C++
runnable Concept定义问题解答 问题背景
想要定义一个runnable concept,要求对应的可调用对象必须能接受一个int类型的第一个参数,后面可跟随任意数量的其他参数。编写的代码无法编译,具体代码及错误信息如下:
错误代码
#include <string> template <typename TASK, typename ...ARGS> concept runnable = requires (TASK task, int n, ARGS... args) { task (n, args...); }; template<runnable TASK> void run (TASK task) {} struct MyTask { int operator() (int n, std::string x, double y) const { return 0; } }; int main (int argc, char** argv) { run (MyTask()); }
编译器错误
error: no matching function for call to 'run' candidate template ignored: constraints not satisfied [with TASK = MyTask] because 'MyTask' does not satisfy 'runnable' because 'task(n, args...)' would be invalid: no matching function for call to object of type 'MyTask' task (n, args...);
用户提出两个问题:
- 是否可以定义这样的concept?
- 如果可以,代码存在什么错误?
补充约束:MyTask::operator()可以包含任意多个参数,但不能是模板函数。
解答
问题1:是否可以定义这样的concept?
可以定义满足需求的concept,只需调整concept的写法即可。
问题2:代码存在的错误
当前runnable concept将ARGS...作为模板参数,这会导致:
- 在使用
runnable<TASK>约束时,编译器无法推导出ARGS...的具体类型——run函数仅指定了TASK,未提供ARGS...的信息,约束检查无法完成。 - 该concept的逻辑是要求对于任意的
ARGS...类型组合,task(n, args...)都能合法调用,但MyTask的operator()只接受固定的int, std::string, double参数,并非任意参数组合,因此约束不满足。
正确实现方式
方式一:嵌套requires表达式(C++20及以上)
通过嵌套requires检查是否存在至少一组参数,使得可调用对象能以int作为第一个参数调用:
#include <string> template <typename TASK> concept runnable = requires(TASK task, int n) { // 检查是否存在任意数量的参数,让task(n, ...)合法 requires requires(auto... args) { task(n, args...); }; }; template<runnable TASK> void run(TASK task) {} struct MyTask { int operator()(int n, std::string x, double y) const { return 0; } }; int main(int argc, char** argv) { run(MyTask()); // 编译通过 }
方式二:提取可调用对象的签名
通过模板特化提取operator()的参数类型,直接判断第一个参数是否为int:
#include <string> #include <tuple> // 辅助模板:提取const成员函数的参数类型 template<typename T> struct callable_args; template<typename ReturnType, typename ClassType, typename... Args> struct callable_args<ReturnType(ClassType::*)(int, Args...) const> { using type = std::tuple<int, Args...>; }; // 定义concept:检查operator()的第一个参数是否为int template<typename TASK> concept runnable = requires { typename callable_args<decltype(&TASK::operator())>::type; }; template<runnable TASK> void run(TASK task) {} struct MyTask { int operator()(int n, std::string x, double y) const { return 0; } }; int main(int argc, char** argv) { run(MyTask()); // 编译通过 }
两种方式说明
- 方式一通用性更强,支持所有可调用类型(包括函数指针、lambda等),只要能以
int作为第一个参数调用即可。 - 方式二更直接,但默认只支持
const成员函数形式的operator(),若要支持非const成员函数、函数指针等,需扩展辅助模板的特化版本。
内容的提问来源于stack exchange,提问作者abcdefg
相关产品推荐
相关产品推荐

