模板类型推导
模板函数可以根据函数参数的类型推导出模板参数的类型,原理和自动类型(auto)推导是一样的。输入是左值的情况:
template <typename T>
void func1(T x) {}
template <typename T>
void func2(T& x) {}
template <typename T>
void func3(T&& x) {}
int main() {
int x = 1024;
func1(x); // int
func2(x); // int&
func3(x); // int&
}
输入是右值的情况:
template <typename T>
void func1(T x) {}
template <typename T>
void func2(const T& x) {}
template <typename T>
void func3(T&& x) {}
int main() {
func1(1024); // int
func2(1024); // const int&
func3(1024); // int&&
}
T&& 是万能引用,原理是引用折叠。引用折叠的规则:如果两个引用都是右值引用,那么结果是右值引用,否则是左值引用。可以通过万能引用实现 std::move():
template <typename T>
decltype(auto) move(T&& x) {
return static_cast<std::remove_reference_t<T>&&>(x);
}
如果 x 是 int 或 int& 类型(左值),则 T 是 int&,T&& 是 int& &&,根据引用折叠,也就是 int&,move() 返回 int&&。实例化的模板函数相当于:
int&& move(int& x) {
return static_cast<int&&>(x);
}
如果 x 是 int&& 类型(右值),则 T 是 int,T&& 是 int&& &&,根据引用折叠,也就是 int&&,move() 返回 int&&。实例化的模板函数相当于:
int&& move(int&& x) {
return static_cast<int&&>(x);
}
还可以通过万能引用实现 std::forward():
template <typename T>
decltype(auto) forward(std::remove_reference_t<T>& x) {
return static_cast<T&&>(x);
}
template <typename T>
void func(T&& x) {
other_func(forward<T>(x));
}
如果 x 是 int 或 int& 类型(左值),则 T 是 int&,T&& 是 int& &&,根据引用折叠,也就是 int&,forward() 返回 int&。实例化的模板函数相当于:
int& forward(int& x) {
return static_cast<int&>(x);
}
void func(int& x) {
other_func(static_cast<int&>(x));
}
如果 x 是 int&& 类型(右值),则 T 是 int,T&& 是 int&& &&,根据引用折叠,也就是 int&&,forward() 返回 int&&。实例化的模板函数相当于:
int&& forward(int& x) {
return static_cast<int&&>(x);
}
void func(int&& x) {
other_func(static_cast<int&&>(x));
}
可变参模板
有时,函数的参数个数是不确定的,可以使用可变参模板实现:
template <typename ...Args> // 打包
void func(Args&& ...args) { // 打包
other_func(std::forward<Args>(args)...); // 解包
}
线程
标准库提供了跨平台的多线程接口。一个简单的无界队列:
template <typename T>
class Queue {
public:
template <typename T>
void push(T&& val) {
std::lock_guard lk{ m_ };
q_.push(std::forward<T>(val));
cv_.notify_one();
}
T pop() {
std::unique_lock lk{ m_ };
cv_.wait(lk, [this] { return !q_.empty(); });
assert(!q_.empty());
T ret{ std::move(q_.front()) };
q_.pop();
return ret;
}
std::optional<T> try_pop() {
std::lock_guard lk{ m_ };
if (q_.empty()) return {};
std::optional ret{ std::move(q_.front()) };
q_.pop();
return ret;
}
private:
std::queue<T> q_;
std::mutex m_;
std::condition_variable cv_;
};
一个简单的有界队列:
template <typename T>
class Queue {
public:
Queue(int capacity) : q_{ capacity } {}
template <typename T>
void push(T&& val) {
std::unique_lock lk{ m_ };
not_full_.wait(lk, [this] { return !q_.full(); });
q_.push_back(std::forward<T>(val));
not_empty_.notify_one();
}
T pop() {
std::unique_lock lk{ m_ };
not_empty_.wait(lk, [this] { return !q_.empty(); });
T ret = std::move(q_.front());
q_.pop_front();
not_full_.notify_one();
return ret;
}
template <typename T>
bool try_push(T&& val) {
std::lock_guard lk{ m_ };
if (q_.full()) return false;
q_.push_back(std::forward<T>(val));
not_empty_.notify_one();
return true;
}
std::optional<T> try_pop() {
std::lock_guard lk{ m_ };
if (q_.empty()) return {};
std::optional ret{ std::move(q_.front()) };
q_.pop_front();
not_full_.notify_one();
return ret;
}
private:
boost::circular_buffer<T> q_;
std::mutex m_;
std::condition_variable not_empty_;
std::condition_variable not_full_;
};
一个简单的计数器:
class CountDown {
public:
CountDown(int n) : n_(n) {}
void wait() {
std::unique_lock lk{ m_ };
cv_.wait(lk, [this] { return n_ == 0; });
}
void down() {
std::lock_guard lk{ m_ };
if (--n_ == 0) cv_.notify_all();
}
private:
int n_;
std::mutex m_;
std::condition_variable cv_;
};
一个简单的线程池:
class ThreadPool {
public:
using task = std::function<void()>;
ThreadPool(int n) : q_{ n }, running_{ false } {}
~ThreadPool() {
if (running_) stop();
}
void start(int n) {
running_ = true;
threads_.reserve(n);
while (n--) {
threads_.push_back(std::thread(&ThreadPool::work, this));
}
}
void stop() {
{
std::lock_guard lk{ m_ };
running_ = false;
not_empty_.notify_all();
not_full_.notify_all();
}
for (auto& t : threads_) {
t.join();
}
}
template <typename T>
void run(T&& t) {
std::unique_lock lk{ m_ };
not_full_.wait(lk, [this] { return !q_.full() || !running_; });
if (!running_) return;
q_.push_back(std::forward<T>(t));
not_empty_.notify_one();
}
private:
void work() {
while (true) {
task t;
{
std::unique_lock lk{ m_ };
not_empty_.wait(lk, [this] { return !q_.empty() || !running_; });
if (!running_) return;
t = std::move(q_.front());
q_.pop_front();
not_full_.notify_one();
}
t();
}
}
std::vector<std::thread> threads_;
boost::circular_buffer<task> q_;
bool running_;
std::mutex m_;
std::condition_variable not_empty_;
std::condition_variable not_full_;
};