为什么互斥体和条件变量很容易复制
Why are mutexes and condition variables trivially copyable?
LWG 2424 讨论了原子组学、互斥体和条件变量在 C++14 中可简单复制的不良状态。我很欣赏已经排好了修复程序,但std::mutex
、std::condition variable
等人似乎有非平凡的析构函数。例如:
30.4.1.2.1 类互斥锁 [thread.mutex.class]
namespace std { class mutex { public: constexpr mutex() noexcept; ~mutex(); // user-provided => non-trivial … } }
这难道不应该取消他们作为微不足道的可复制的资格吗?
是我的错误,要么是我被错误引用了,老实说我不记得是哪个。
但是,我对这个问题有非常强烈的建议:
不要使用
is_trivial
,也不要使用is_trivially_copyable
! 曾!!!
请改用以下方法之一:
is_trivially_destructible<T>
is_trivially_default_constructible<T>
is_trivially_copy_constructible<T>
is_trivially_copy_assignable<T>
is_trivially_move_constructible<T>
is_trivially_move_assignable<T>
理由:
TLDR:看到这个很好的问题和正确的答案。
没有人(包括我自己)能记住is_trivial
和is_trivially_copyable
的定义. 如果你碰巧查了一下,然后花10分钟分析它,它可能会也可能不会做你直觉认为它做的事情。 如果您设法正确分析它,CWG 很可能会在很少或没有通知的情况下更改其定义并使您的代码无效。
使用is_trivial
和is_trivially_copyable
就是在玩火。
但是这些:
is_trivially_destructible<T>
is_trivially_default_constructible<T>
is_trivially_copy_constructible<T>
is_trivially_copy_assignable<T>
is_trivially_move_constructible<T>
is_trivially_move_assignable<T>
完全按照他们听起来的样子去做,并且不太可能改变他们的定义。 不得不单独处理每个特殊成员似乎过于冗长。 但它会在代码的稳定性/可靠性方面得到回报。 如果必须,请将这些个人特征打包成自定义特征。
更新
例如,clang & gcc 编译了这个程序:
#include <type_traits>
template <class T>
void
test()
{
using namespace std;
static_assert(!is_trivial<T>{}, "");
static_assert( is_trivially_copyable<T>{}, "");
static_assert( is_trivially_destructible<T>{}, "");
static_assert( is_destructible<T>{}, "");
static_assert(!is_trivially_default_constructible<T>{}, "");
static_assert(!is_trivially_copy_constructible<T>{}, "");
static_assert( is_trivially_copy_assignable<T>{}, "");
static_assert(!is_trivially_move_constructible<T>{}, "");
static_assert( is_trivially_move_assignable<T>{}, "");
}
struct X
{
X(const X&) = delete;
};
int
main()
{
test<X>();
}
请注意,X
是可复制的,但不能简单复制可构造。 据我所知,这是顺从行为。
VS-2015目前表示X
既不能轻易复制,也不能轻易复制可构造。 根据当前的规范,我相信这是错误的,但它肯定符合我的常识告诉我的。
如果我需要memcpy
未初始化的内存,我会相信is_trivially_copy_constructible
而不是is_trivially_copyable
向我保证这样的操作是可以的。 如果我想memcpy
初始化的内存,我会检查is_trivially_copy_assignable
。
并非所有实现都为 mutex
提供非平凡析构函数。 参见libstdc++(并假设已经定义了__GTHREAD_MUTEX_INIT
):
// Common base class for std::mutex and std::timed_mutex
class __mutex_base
{
// […]
#ifdef __GTHREAD_MUTEX_INIT
__native_type _M_mutex = __GTHREAD_MUTEX_INIT;
constexpr __mutex_base() noexcept = default;
#else
// […]
~__mutex_base() noexcept { __gthread_mutex_destroy(&_M_mutex); }
#endif
// […]
};
/// The standard mutex type.
class mutex : private __mutex_base
{
// […]
mutex() noexcept = default;
~mutex() = default;
mutex(const mutex&) = delete;
mutex& operator=(const mutex&) = delete;
};
mutex
的这种实现既符合标准又易于复制(可以通过 Corilu 进行验证)。同样,没有什么能阻止实现保持condition_variable
微不足道的可破坏性(参见 [thread.condition.condvar]/6,尽管我找不到这样做的实现)。
底线是,我们需要明确的、规范性的保证,而不是对condition_variable
做什么或不需要做什么(以及它必须如何做到这一点)进行聪明、微妙的解释。
从语言律师的角度来看待这个问题很重要。
实现基本上不可能实现mutex
、条件变量等,使它们易于复制。在某些时候,你必须编写一个析构函数,而这个析构函数很可能必须做一些不平凡的工作。
但这并不重要。为什么?因为该标准没有明确说明此类类型不会是微不足道的可复制的。因此,从标准的角度来看,理论上这些对象是可以轻易复制的。
尽管没有功能实现,但N4460的重点是非常清楚地表明,此类类型永远不会被简单地复制。
- 从结构寻址时,MMAP变量的行为很奇怪
- C++(.cpp文件和.h文件)拆分代码并添加一个函数,提取 - 这很容易吗?
- 使用额外的变量使计算更容易理解 - (多少)我减慢了代码的速度?
- 为许多类可能需要的所有常量变量制作独立的头文件是否是一种很好的做法?
- 给定一个无符号整数,很容易交换每个偶数位和奇数位。这可以推广到交换每个偶数和奇数n位吗?
- 3 * 1000000000 溢出为 int,但变量很长.为什么
- C 很容易使用枚举进行标志和位操作
- 很难从文件中读取行并将其放入变量(C )
- 为什么constexpr函数能很好地与运行时生成的变量配合使用
- 为什么互斥体和条件变量很容易复制
- C++.Net程序集可以很容易地反编译吗
- 指针问题(可能很容易)
- 我可以很容易地创建一个模板函数,它接受任意类型的任意容器并对其进行操作
- 结构体向量的问题:解约束迭代器(可能很容易)
- 使用非常量变量声明局部数组来设置长度,这很有效
- 为什么运行得很好?(范围外变量的访问地址)
- 是否有一个很好的方法找到两个变量的模数使用SSE?(没有SVML)
- 为什么变量声明可以很好地作为for循环的条件
- 2D数组内存泄漏-应该很容易,我觉得愚蠢
- 为什么转换变量函数参数很重要?