You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否将类内定义的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 22:17:46