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
! EVER !!!
而是使用以下其中一个:
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
是标准的符合和平凡的可复制(可以验证 通过Coliru)。同样,没有什么可以阻止实现保持 condition_variable
平凡的可破坏的(参见 [thread.condition.condvar] / 6,虽然我找不到一个实现)。
最重要的是,我们需要明确的,规范的保证,而不是对内容的巧妙,微妙的解释 condition_variable
做或不做(以及如何做到这一点)。
从语言律师的角度来看这一点非常重要。
实现基本上不可能实现 mutex
,条件变量等,使它们可以轻易地复制。在某些时候,你必须编写一个析构函数,而析构函数很可能不得不做一些非平凡的工作。
但这没关系。为什么?因为标准没有明确声明这些类型不会轻易复制。因此,从标准的角度来看, 理论上可行 使这些对象可以轻易复制。
尽管每个都没有功能实现,但N4460的要点是要明确这些类型永远不会轻易复制。