C++ 11 Lambda表达式 - 滴水瓦 - 博客园
Lambda表达式完整的声明格式如下:
[capture list] (params list) mutable exception-> return type { function body }
各项具体含义如下
此外,我们还可以省略其中的某些成分来声明“不完整”的Lambda表达式,常见的有以下几种:
序号 | 格式 |
---|---|
1 | [capture list] (params list) -> return type {function body} |
2 | [capture list] (params list) {function body} |
3 | [capture list] {function body} |
其中:
格式3中省略了参数列表,类似普通函数中的无参函数。
讲了这么多,我们还没有看到Lambda表达式的庐山真面目,下面我们就举一个实例。
#include
#include
#include
using namespace std;
bool cmp(int a, int b)
{
return a < b;
}
int main()
{
vector myvec{ 3, 2, 5, 7, 3, 2 };
vector lbvec(myvec);
sort(myvec.begin(), myvec.end(), cmp); // 旧式做法
cout << "predicate function:" << endl;
for (int it : myvec)
cout << it << ' ';
cout << endl;
sort(lbvec.begin(), lbvec.end(), [](int a, int b) -> bool { return a < b; }); // Lambda表达式
cout << "lambda expression:" << endl;
for (int it : lbvec)
cout << it << ' ';
}
头文件在
构造函数:
default (1) | thread() noexcept; |
---|---|
initialization (2) | template |
copy [deleted] (3) | thread (const thread&) = delete; |
move (4) | thread (thread&& x) noexcept; |
使用实例:
实例一:
#include
#include
#include
#include
#include
#include
void f1(int n)
{
for (int i = 0; i < 5; ++i) {
std::cout << "Thread " << n << " executing\n";
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void f2(int& n)
{
for (int i = 0; i < 5; ++i) {
std::cout << "Thread 2 executing\n";
++n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
int main()
{
int n = 0;
std::thread t1; // t1 is not a thread
std::thread t2(f1, n + 1); // pass by value
std::thread t3(f2, std::ref(n)); // pass by reference
std::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a thread
t2.join();
t4.join();
std::cout << "Final value of n is " << n << '\n';
}
实例二:
#include
#include
#include
void hello_thread()
{
std::cout << "hello thread" << std::endl;
}
int main(int argc, char* argv[])
{
std::thread t1(hello_thread);
t1.join();
std::cout << "Main here" << std::endl;
return 0
}
move (1) | thread& operator= (thread&& rhs) noexcept; |
---|---|
copy [deleted] (2) | thread& operator= (const thread&) = delete; |
template< class T >
typename std::remove_reference
std::move返回的是一个右值引用的变量,而函数的作用是将t到另一对象有效率的资源传递,传递过后,资源t会被销毁。在实例一中的作用就是将线程t3的资源传递给t4,并销毁t3。
join会阻塞当前的线程,直到运行的线程结束,比如在main函数里面调用线程thread,那么main函数里面调用thread后,会先去执行thread中的代码逻辑,直到其结束,再去执行main函数里面的代码逻辑。调用join后,所有分配的资源都会被释放。在调用了join之后,*this就不会拥有任何线程了。 而detach不会阻塞主线程,主线程和子线程是同时运行的
获取线程 ID。为 std::thread::id 类型值
检查线程是否可被 join。
Join 线程。
Detach 线程
Swap 线程 。
返回 native handle。
检测硬件并发特性。返回实现所支持的并发线程数
========================================================
1. explicit关键字 的作用就是防止类构造函数的隐式自动转换
explicit ConfigurationFileResolver(
const std::vector& configuration_files_directories);
2. std::lround 返回最接近x的long int整数 eg: lround(15.2) -> 15, lround(15.8) -> 16
// 返回最接近x的整数,四舍五入
inline int RoundToInt(const float x) { return std::lround(x); }
3. decltype 可以从一个变量或表达式中得到类型(这里不是很明白)
// 在 C++11 中, auto可以和decltype一起用, 将auto放置在返回值类型上当做占位符, 不表达实际意思
// 在参数列表后添加 -> decltype( ), 这是后置返回类型, 代表返回的类型是 () 中的类型
// 在 C++14 中, 则可以不用 decltype
template
auto GetTimeImpl(const T& t, int) -> decltype(t.time()) {
return t.time();
}
4. std::bidirectional_iterator_tag 用于将迭代器的类别标识为双向迭代器
using iterator_category = std::bidirectional_iterator_tag;
5. static 修饰 成员函数, 代表静态成员函数
// 静态成员函数是属于类的, 不是属于对象的. 静态成员函数只能访问静态成员变量
// 只有 NodeId 和 SubmapId 才可以当做 MapById 的key
static int GetIndex(const NodeId& id) { return id.node_index; }
6. // c++11: override 关键字告诉编译器, 该函数应覆盖基类中的函数.
// 如果该函数实际上没有覆盖任何函数, 则会导致编译器错误
// 如果没加这个关键字 也没什么严重的error 只是少了编译器检查的安全性
// Returns the time of the last added pose or Time::min() if no pose was added
// yet.
common::Time GetLastPoseTime() const override;
7. std::unique_ptr 是独享被管理对象指针所有权的智能指针
// 它无法复制到其他 unique_ptr, 也无法通过值传递到函数,也无法用于需要副本的任何标准模板库 (STL) 算法
// 只能通过 std::move() 来移动unique_ptr
// std::make_unique 是 C++14 才有的特性
std::unique_ptr imu_tracker_; // 保存与预测当前姿态
8. std::function 通用多态函数封装器
// std::function 的实例能存储、复制及调用任何可调用 (Callable) 目标:
// 如函数、 lambda表达式、 bind表达式或其他函数对象, 还有指向成员函数指针和指向数据成员指针.
// 它也是对 C++ 中现有的可调用实体的一种类型安全的包裹(相对来说, 函数指针的调用不是类型安全的)
9. using in c++11: c++11 的using可以用于模板部分具体化
// A callback which is called after local SLAM processes an accumulated
// 'sensor::RangeData'. If the data was inserted into a submap, reports the
// assigned 'NodeId', otherwise 'nullptr' if the data was filtered out.
using LocalSlamResultCallback =
std::function)>;
10. 限域枚举 enum class
enum class SensorType {
RANGE = 0,
IMU,
ODOMETRY,
FIXED_FRAME_POSE,
LANDMARK,
LOCAL_SLAM_RESULT
};
11. reinterpret_cast 用于进行各种不同类型的指针之间、不同类型的引用之间以及指针和能容纳指针的整数类型之间的转换
// MakeUniqueCairoSurfacePtr 生成一个指向cairo_surface_t数据的指针
auto surface = MakeUniqueCairoSurfacePtr(
// cairo_image_surface_create_for_data: 根据提供的像素数据创建surface, 返回指向新创建的surface的指针
cairo_image_surface_create_for_data(
reinterpret_cast(cairo_data->data()), kCairoFormat, width,
height, expected_stride) );
12. std::array::data() 返回指向数组对象中第一个元素的指针
problem.AddParameterBlock(C_submaps.at(submap_id_data.id).data(), 3);
13. 匿名命名空间, 作用域被限制在本文件内
namespace { }
14. static_cast关键字(编译时类型检查): static_cast < type-id > ( expression )
* 该运算符把expression转换为type-id类型, 但没有运行时类型检查来保证转换的安全性
(1)用于基本数据类型之间的转换, 如把int转换为char, 把int转换成enum,
(2)把空指针转换成目标类型的空指针
(3)把任何类型的表达式类型转换成void类型
(4)用于类层次结构中父类和子类之间指针和引用的转换.
15. dynamic_cast关键字(运行时类型检查): dynamic_cast < type-id > ( expression )
该运算符把 expression 转换成 type-id 类型的对象. Type-id必须是类的指针、类的引用或者void *
如果type-id是类指针类型, 那么expression也必须是一个指针
如果type-id是一个引用, 那么expression也必须是一个引用
dynamic_cast主要用于类层次间的上行转换(子类到父类)和下行转换(父类到子类), 还可以用于类之间的交叉转换.
在类层次间进行上行转换时, dynamic_cast和static_cast的效果是一样的;
在进行下行转换时, dynamic_cast具有类型检查的功能, 比static_cast更安全.
16. std::lower_bound() 是在区间内找到第一个大于等于 value 的值的位置并返回, 如果没找到就返回 end() 位置
// 在第四个参数位置可以自定义比较规则,在区域内查找第一个 **不符合** comp 规则的元素
// 在imu数据队列中找到第一个时间上 大于等于 imu_tracker->time() 的数据的索引
auto it = std::lower_bound(
imu_data_.begin(), imu_data_.end(), imu_tracker->time(),
[](const sensor::ImuData& imu_data, const common::Time& time) {
return imu_data.time < time;
});
17. std::prev 获取一个距离指定迭代器 n 个元素的迭代器,而不改变输入迭代器的值
// 默认 n 为1,当 n 为正数时, 其返回的迭代器将位于 it 左侧;
// 反之, 当 n 为负数时, 其返回的迭代器位于 it 右侧
// 获取 [0, n-1] 范围的预测位姿
for (auto it = times.begin(); it != std::prev(times.end()); ++it) {
poses.push_back(ExtrapolatePose(*it).cast());
}
18. template
// 函数模板的调用使用 实参推演 来进行
// 类模板 模板形参的类型必须在类名后的尖括号中明确指定, 不能使用实参推演
// 在类外声明一个 函数模板, 使用 实参推演 的方式来使得 类模板可以自动适应不同的数据类型
// 根据传入的data的数据类型,自动推断DataType, 实现一个函数处理不同类型的传感器数据
template
std::unique_ptr> MakeDispatchable(
const std::string &sensor_id, const DataType &data) {
return absl::make_unique>(sensor_id, data);
}
19. auto*(指针类型说明符), auto&(引用类型说明符), auto &&(右值引用)
// 获取当前队列中时间最老的一个的一个数据
const auto* data = it->second.queue.Peek();
20. map::emplace() 返回的 pair 对象
// pair 的成员变量 first 是一个指向插入元素或阻止插入的元素的迭代器
// 成员变量 second 是个布尔值, 表示是否插入成功, 如果这个元素的索引已经存在插入会失败,返回false
auto emplace_result = common_start_time_per_trajectory_.emplace(
trajectory_id, common::Time::min());
common::Time& common_start_time = emplace_result.first->second;
21. 移动构造函数, 只在使用的时候编译器才会自动生成
// 这里是显示指定让编译器生成一个默认的移动构造函数
OrderedMultiQueue(OrderedMultiQueue&& queue) = default;
22. std::shared_ptr 主要的用途就是方便资源的管理, 自动释放没有指针引用的资源
// 使用引用计数来标识是否有其余指针指向该资源.(注意, shart_ptr本身指针会占1个引用)
// 引用计数是分配在动态分配的, std::shared_ptr支持拷贝, 新的指针获可以获取前引用计数个数
23. lambda表达式
[node, handler, trajectory_id, topic](const typename MessageType::ConstPtr& msg) {
(node->*handler)(trajectory_id, topic, msg);
}
24. std::forward_as_tuple tuple的完美转发
// 该 tuple 在以右值为参数时拥有右值引用数据成员, 否则拥有左值引用数据成员
// c++11: std::piecewise_construct 分次生成tuple的标志常量
// 在map::emplace()中使用forward_as_tuple时必须要加piecewise_construct,不加就报错
// https://www.cnblogs.com/guxuanqing/p/11396511.html
// 以1ms, 以及重力常数10, 作为参数构造PoseExtrapolator
extrapolators_.emplace(
std::piecewise_construct,
std::forward_as_tuple(trajectory_id),
std::forward_as_tuple(
::cartographer::common::FromSeconds(kExtrapolationEstimationTimeSec),
gravity_time_constant));
25. =delete: 禁止编译器自动生成默认函数; =default: 要求编译器生成一个默认函数
// 禁止编译器自动生成 默认拷贝构造函数(复制构造函数)
Node(const Node&) = delete;
// 禁止编译器自动生成 默认赋值函数
Node& operator=(const Node&) = delete;
26. std::unordered_map 是采用哈希数据结构实现的, 内部数据保存是无序的
// 相对于std::map, unordered_map的查找和插入的效率高, 时间复杂度为常数级别O(1),而额外空间复杂度则要高出许多
// 对于需要高效率查询的情况, 使用unordered_map容器, 但是unordered_map对于迭代器遍历效率并不高
// These are keyed with 'trajectory_id'.
std::map extrapolators_;
std::map last_published_tf_stamps_;
std::unordered_map sensor_samplers_;
std::unordered_map> subscribers_;
27. std::tie()函数可以将变量连接到一个给定的tuple上,生成一个元素类型全是引用的tuple
// 根据Lua配置文件中的内容, 为node_options, trajectory_options 赋值
std::tie(node_options, trajectory_options) =
LoadOptions(FLAGS_configuration_directory, FLAGS_configuration_basename);
28. std::move 是将对象的状态或者所有权从一个对象转移到另一个对象,
// 只是转移, 没有内存的搬迁或者内存拷贝所以可以提高利用效率,改善性能..
// 右值引用是用来支持转移语义的.转移语义可以将资源 ( 堆, 系统对象等 ) 从一个对象转移到另一个对象,
// 这样能够减少不必要的临时对象的创建、拷贝以及销毁, 能够大幅度提高 C++ 应用程序的性能.
// 临时对象的维护 ( 创建和销毁 ) 对性能有严重影响.
// Node类的初始化, 将ROS的topic传入SLAM, 也就是MapBuilder
Node node(node_options, std::move(map_builder), &tf_buffer,
FLAGS_collect_metrics);