为什么赫伯·萨特(Herb Sutter)的监视器示例采用"T"而不是"T&"或"T&&"?
Why does Herb Sutter's monitor example take a 'T' rather than 'T&' or 'T&&'?
在 40:30 在 C++ 及以后 2012: Herb Sutter - C++ 并发,Herb Sutter 根据他的包装器习语展示了一个监视器类:
template<typename T>
class monitor {
private:
mutable T t;
mutable std::mutex m;
public:
monitor(T t_ = T{}) : t{t_} {}
template<typename F>
auto operator()(F f) const -> decltype(f(t))
{
std::lock_guard<mutex> _{m}; return f(t);
}
}
请注意构造函数接受T
而不是T&
或T&&
,并且不包括构造函数。我想象的用例是:
monitor<Foo> foo = Foo(...);
由于缺少移动构造函数而失败。
Sutter 示例中的构造函数采用T
而不是T &
或T&&
,因为值语义是C++中的默认值,并且:
- 没有特别的理由假设您希望在构造时移动
T
。(有时,可能会有 - 但显然不是在这个演讲中)。 - 您绝对不想采取
T&
- 这意味着其他人将访问您受监控的数据而不去监视器,即不锁定,即不序列化访问......
只有赫伯·萨特才能真正回答这个问题。
如果我不得不猜测,我会说他的理由是:
- 它必须适合幻灯片。
- 包括转发构造函数和in_place构造函数只会使读者感到困惑,并减损演示监视模式的重点。
如果需要此代码来支持不可复制和不可移动的类型,请查看std::optional
如何实现对它们的支持可能会有所帮助。
也就是说,std::optional
有两个构造函数重载。
-
一个构造函数重载采用转发引用(即
U&&
) 并使用std::forward
来构造包装类型。这增加了对不可复制类型的支持。 -
另一个构造函数重载采用标记类型,
std::in_place
,并将所有剩余的参数直接forward
到包装类型的构造函数。 这用于就地构造包装类型,因此永远不需要移动它。
下面是一些示例代码: https://godbolt.org/g/hWmcTA
#include <utility>
#include <mutex>
template<typename T>
class monitor {
private:
mutable T t;
mutable std::mutex m;
public:
monitor()
: t{}
{ }
template<typename Y>
monitor(Y&& y)
: t{std::forward<Y>(y)}
{ }
template<typename... Args>
monitor(std::in_place_t, Args&&... args)
: t{std::forward<Args>(args)...}
{ }
template<typename F>
auto operator()(F f) const -> decltype(f(t))
{
std::lock_guard<std::mutex> _{m}; return f(t);
}
};
// A non-movable type, just for testing.
struct NonMovable {
NonMovable(int n = 0, double d = 0)
: n_{n}, d_{d}
{ }
NonMovable(const NonMovable&) = delete;
NonMovable(NonMovable&&) = delete;
NonMovable& operator=(const NonMovable&) = delete;
NonMovable& operator=(NonMovable&&) = delete;
private:
int n_;
double d_;
};
int main() {
// Non-movable type.
monitor<NonMovable> m4; //< Good. Uses default constructor.
//monitor<NonMovable> m5{NonMovable{1, 2.2}};//< Bad! Forwarding constructor.
monitor<NonMovable> m6{std::in_place, 1, 2.2};//< Good. In-place constructor.
// And a movable type, just to make sure we didn't break anything.
monitor<int> m1; //< Good. Uses default constructor.
monitor<int> m2{1}; //< Good. Forwarding constructor.
monitor<int> m3{std::in_place, 1}; //< Good. In-place constructor.
}
相关文章:
- 编译要在英特尔Hyperscan中使用的.cc文件时出现问题
- 在卡萨布兰卡形成编码参数的列表
- 将gsl c++程序与"英特尔MKL"链接
- 使用英特尔 PIN 修改寄存器
- 使用英特尔内联函数将打包的 8 位整数乘以浮点数向量
- 如何使用英特尔 PIN 捕获阵列的所有负载?
- 库特<<恩德尔;不适用于打印 2D 阵列
- 库特无符号字符
- 英特尔 TBB 程序不会终止,可能会误用参考计数器
- GCC/CLang不同意模板模板参数的部分特化
- 与卡特琳娜一起卷曲C++失败并得到"Undefined symbols for architecture x86_64"
- 将"-01"替换为"-02" 英特尔编译器选项会导致 FPE 在较小的 for 循环行程计数中抛出
- 如何在贝尔曼福特算法中检测负循环?
- 我如何创建一个函数(),其中一帕雷米特是映射
- 特里树.无法访问内存
- 在 Azure DevOps 构建管道中使用英特尔C++编译器为 Linux 环境构建C++代码
- 为什么赫伯·萨特(Herb Sutter)的监视器示例采用"T"而不是"T&"或"T&&"?
- 赫伯·萨特在 GotW #35 中关于字体名的代码笑话过时了吗?
- 赫伯·萨特原子武器"Why Standalone Fences are Suboptimal"
- 波特迪奥和阿尔萨:"Cannot obtain info for CTL elem"