1. 函数指针只能指向 无捕获的Lambda
Lambda 表达式如果 没有捕获外部变量,它就可以隐式转换为普通函数指针。
✅ 示例:
void callFunc(int (*func)(int)) {
std::cout << func(10) << std::endl;
}
int main() {
auto lambda = [](int x) { return x * 2; }; // 无捕获
callFunc(lambda); // OK!
return 0;
}
✅ 等价于:
int (*funcPtr)(int) = [](int x) { return x * 2; };
funcPtr(5); // 返回 10
✅ 原因: 无捕获 Lambda 会被编译器转换成一个普通函数,和函数指针兼容。
❌ 捕获变量的 Lambda 不行!
int base = 100;
auto lambda = [base](int x) { return base + x; }; // 捕获了 base
// callFunc(lambda); // ❌ 错误:不能将带捕获的 lambda 转换为函数指针
❌ 带捕获的 Lambda 是一个有状态的匿名类对象,不能隐式转换成函数指针。
💡 想传递捕获 Lambda?用 std::function
!
#include <functional>
void callFunc(std::function<int(int)> func) {
std::cout << func(10) << std::endl;
}
int main() {
int base = 100;
auto lambda = [base](int x) { return base + x; }; // 有捕获
callFunc(lambda); // OK!
}
🧠 总结:
Lambda 类型 | 可转换为函数指针 | 原因 |
---|---|---|
无捕获 [ ] | ✅ 可以 | 编译器生成的是静态函数 |
有捕获 [=], [&] | ❌ 不可以 | 生成的是闭包对象 |
2. 类内成员函数为何能访问同类私有成员
#include <thread>
#include <iostream>
class A {
public:
A(int i) : _i{i} {}
A(const A & other) //error const 不能被移动
{
_i = other._i;
_t = std::move(other._t);
}
int GetIndex() const { return _i; }
private:
std::thread _t;
int _i;
};
int main(int argc, char **argv)
{
A a(1);
std::cout << "member _i is " << a.GetIndex() << std::endl;
return 0;
}
_t
和 _i
都是私有成员(private
),为什么在 A(const A& other)
中可以访问 other._i
和 other._t
?
答案其实是这样的:
✅ 在类的成员函数内部访问同类对象的私有成员是合法的
这是 C++ 的一个语言特性。
在类的成员函数中(包括构造函数、析构函数、成员方法等),你可以访问:
- 当前对象的私有/保护成员(这是显然的);
- 其他同类对象(即同类型实例)的私有/保护成员。
💡 举个例子说明:
class Example {
private:
int _x;
public:
Example(int x) : _x{x} {}
void CopyFrom(const Example& other) {
_x = other._x; // ✅ 合法,虽然 _x 是私有的
}
};
这里 other._x
是私有成员,但 CopyFrom()
是 Example
的成员函数,所以它可以访问 任意 Example
对象的私有成员。
🤔 为什么 C++ 要这么设计?
因为:
- 成员函数被认为是“代表整个类的行为”;
- 所以,它可以操作同类的其他实例的内部状态;
- 这为实现拷贝构造、比较、赋值运算符重载等提供了必要能力。
🧠 类比一下
你可以把这个设计类比成“成员函数 = 内部朋友”,它有资格互相访问类内的隐私。所以并不是“在类外访问”,而是“在类内部函数中访问别的对象的私有成员”。
✅ 所以这行代码是没问题的:
_i = other._i; // 合法,_i 是私有成员,但我们在类的成员函数中访问
但你之前提到的 _t = std::move(other._t)
出问题不是因为访问权限,而是因为 other
是 const
,不能对 const
对象的成员调用 std::move()
(移动会修改对象状态)。