能否将类内定义的friend函数作为std::thread线程函数?
类内友元函数作为线程函数导致编译失败
我是C新手,正在学习线程相关概念,参考《Anthony Williams - C Concurrency in Action》中的Listing 3.6编写示例代码,但无法正常编译,推测问题出在调用类内定义的友元函数作为线程函数上。
我的代码
#include <iostream> #include <mutex> #include <thread> class some_big_object { }; void swap(some_big_object &lhs, some_big_object &rhs); class X { private: some_big_object some_detail; std::mutex m; public: X(some_big_object const &sd) : some_detail(sd) {} friend void swap(X &lhs, X &rhs) { if (&lhs == &rhs) { return; } std::lock(lhs.m, rhs.m); std::lock_guard<std::mutex> lock_a(lhs.m, std::adopt_lock); std::lock_guard<std::mutex> lock_b(rhs.m, std::adopt_lock); swap(lhs.some_detail, rhs.some_detail); } }; int main(void) { some_big_object s1; some_big_object s2; X a(s1); X b(s2); std::thread t1(swap(a, b)); t1.join(); return 0; }
编译错误(翻译后)
error: 没有匹配的函数调用‘std::thread::thread(void)’
核心原因:std::thread构造函数需要传入可调用对象(函数指针、lambda、仿函数等),但你直接执行了swap(a,b)——这会立刻调用该函数并返回void,相当于给线程构造函数传递了一个void值,没有对应的构造函数重载能匹配。
额外问题:类内定义的友元函数swap(X&, X&)在类外部没有自动可见性,即使尝试取函数地址,直接写swap也可能无法被编译器正确查找。
解决方案
1. 修正线程构造的调用方式
不要直接执行函数,而是传递可调用对象和参数,推荐两种方式:
方式一:使用Lambda表达式(最简洁直观)
Lambda内部可以通过**参数依赖查找(ADL)**自动找到类的友元swap函数,同时自然传递引用:
std::thread t1([&a, &b](){ swap(a, b); });
方式二:显式传递函数地址与引用参数
通过static_cast指定函数类型来帮助编译器找到友元函数,并用std::ref包装参数以传递引用:
std::thread t1(static_cast<void(*)(X&, X&)>(swap), std::ref(a), std::ref(b));
(需要包含头文件<functional>以使用std::ref)
2. 补充some_big_object的swap函数定义
当前代码仅声明了swap(some_big_object&, some_big_object&)但未定义,链接阶段会报错,需补充定义:
void swap(some_big_object &lhs, some_big_object &rhs) { // 这里根据需求实现交换逻辑,示例中可留空 }
修正后的完整代码
#include <iostream> #include <mutex> #include <thread> #include <functional> class some_big_object { }; void swap(some_big_object &lhs, some_big_object &rhs) { // 实现交换逻辑 } class X { private: some_big_object some_detail; std::mutex m; public: X(some_big_object const &sd) : some_detail(sd) {} friend void swap(X &lhs, X &rhs) { if (&lhs == &rhs) { return; } std::lock(lhs.m, rhs.m); std::lock_guard<std::mutex> lock_a(lhs.m, std::adopt_lock); std::lock_guard<std::mutex> lock_b(rhs.m, std::adopt_lock); swap(lhs.some_detail, rhs.some_detail); } }; int main(void) { some_big_object s1; some_big_object s2; X a(s1); X b(s2); // 使用Lambda方式创建线程 std::thread t1([&a, &b](){ swap(a, b); }); t1.join(); return 0; }
关键说明
- 线程构造的核心误区:
std::thread的参数是可调用对象+参数列表,而非直接执行函数的结果。 - 类内友元函数的可见性:类内定义的友元函数不会自动暴露到外部作用域,Lambda或显式类型转换可以解决查找问题。
std::ref的作用:std::thread默认会拷贝参数,用std::ref可以确保传递的是引用,匹配swap函数的参数要求。
内容的提问来源于stack exchange,提问作者lahirupw
相关产品推荐
相关产品推荐

