代码如下:
#include <iostream>
#include <type_traits>
class A
{
A() = default;
A(const A&) = default;
A(A&&) = default;
A& operator=(const A&) = default;
A& operator=(A&&) = default;
~A() = default;
int a;
};
int main()
{
std::cout << std::boolalpha <<
std::is_trivially_copy_assignable_v<A> << " " <<
std::is_trivially_copy_constructible_v<A> << " " <<
std::is_trivially_move_assignable_v<A> << " " <<
std::is_trivially_move_constructible_v<A> << " " <<
std::is_trivially_destructible_v<A> << " " <<
std::is_trivially_copyable_v<A> << "\n";
return 0;
}
Run Code Online (Sandbox Code Playgroud)
And the output is false false false false false …
除了非平凡的析构函数之外,所有特殊函数都默认的类不是可简单移动或复制构造的。有关示例,请参见https://godbolt.org/z/o83rPz:
#include <type_traits>
class Sample
{
public:
Sample(Sample const&) = default;
Sample(Sample&&) = default;
Sample& operator=(Sample const&) = default;
Sample& operator=(Sample&&) = default;
~Sample() {}
};
static_assert(std::is_copy_constructible<Sample>::value, "");
static_assert(std::is_move_constructible<Sample>::value, "");
static_assert(std::is_trivially_copy_constructible<Sample>::value, ""); // Fails with GCC and Clang
static_assert(std::is_trivially_move_constructible<Sample>::value, ""); // Fails with GCC and Clang
static_assert(std::is_copy_assignable<Sample>::value, "");
static_assert(std::is_move_assignable<Sample>::value, "");
static_assert(std::is_trivially_copy_assignable<Sample>::value, "");
static_assert(std::is_trivially_move_assignable<Sample>::value, "");
Run Code Online (Sandbox Code Playgroud)
GCC 和 Clang 都没有通过相应的断言,而 ICC 通过。奇怪的是,分配没有受到影响,尽管我可以理解分配给的对象需要被销毁。但反过来说似乎是对的。为什么?为什么ICC不同意?
一个普通可复制的类是一个类:
(1.1) 至少有一个合格的复制构造函数、移动构造函数、复制赋值运算符或移动赋值运算符([special]、[class.copy.ctor]、[class.copy.assign]),
(1.2) 其中每个合格的复制构造函数、移动构造函数、复制赋值运算符和移动赋值运算符都是微不足道的,并且
(1.3) 有一个简单的、未删除的析构函数 ([class.dtor])。
现在,我不完全确定这意味着什么。是否意味着拥有其中之一就足够了?例如,一个具有简单复制构造函数和复制赋值运算符以及显式删除的移动构造函数和移动赋值运算符的类是可以简单复制的,或者这意味着我有未删除的“big 6”并且它们中的每一个都是平凡的?
如果我从字面上理解这一点,那么只有一个构造函数或赋值运算符就足够了。根据cppreference在 c++20 之前就是这种情况。如果没有任何改变(即我仍然可以删除赋值运算符或构造函数),为什么措辞会改变?pre c++20 和 C++20 标准含义有什么区别?
实验(例如Spencer 答案中的实验)表明我的猜测是正确的。我不明白的是 - 为什么要改变 C++20 标准中的措辞。实际上有什么改变吗?
c++17 定义的链接。
在 c++17 中,定义是:
- 一个普通可复制的类是一个类:
(6.1) 其中每个复制构造函数、移动构造函数、复制赋值运算符和移动赋值运算符 ([class.copy], [over.ass]) 要么被删除,要么被忽略,
(6.2) 具有至少一个非删除的复制构造函数、移动构造函数、复制赋值运算符或移动赋值运算符,并且
(6.3) 有一个简单的、未删除的析构函数。
旧定义和新定义之间是否存在细微差别?
示例代码可以在下面或godbolt上找到。假设我们有 4 个班级:
S<T>:持有数据成员。
SCtor<T>:持有数据成员并具有模板构造函数。
SCtorMutable<T>:持有可变数据成员并具有模板构造函数。
SCtorDefault<T>:持有一个成员,有一个模板构造函数,有默认的复制/移动构造函数和默认的复制/移动赋值运算符。
所有编译器都同意这 4 个类是可以简单复制的。
如果有一个简单的包装类W<T>将上述任何一个类作为数据成员。包装类W<S...<T>>仍然是可简单复制的。
如果有另一个包装类WMutable<T>将上述类中的任何一个保存为可变数据成员。
WMutable<S...<T>>是可以简单复制的。WMutable<S<T>>是可以复制的。WMutable<SCtor...<T>>不是可简单复制构造的,因此也不是可简单复制的。WMutable<S<T>>是可以简单复制的。WMutable<SCtor...<T>>不是可简单复制构造的,而是可简单复制的。应该WMutable<T>是可以简单复制的吗?
#include <type_traits>
#include <utility>
template<typename T>
struct S {
T m_t;
};
template<typename T>
struct SCtor {
T m_t;
template<typename... U>
SCtor(U&&... u): m_t(std::forward<U>(u)...) {}
};
template<typename T>
struct SCtorMutable {
mutable T m_t;
template<typename... U> …Run Code Online (Sandbox Code Playgroud) c++ mutable copy-constructor language-lawyer trivially-copyable
Cppreference 指出,关于std::memcpy()(强调):
如果对象可能重叠或不可平凡复制,则 的行为
memcpy未指定并且可能未定义。
因此,我总是在使用之前检查以确保对象是可简单复制的memcpy(),如下所示:
#include <type_traits>
static_assert(std::is_trivially_copyable<T>::value, "Type T must "
"be a trivially-copyable type in order to guarantee that `memcpy()` is safe "
"to use on it.");
memcpy(&outputData, &data, sizeof(data));
Run Code Online (Sandbox Code Playgroud)
std::copy()然而,似乎没有这个限制:https://en.cppreference.com/w/cpp/algorithm/copy。
类型必须可简单复制才能不具有未定义行为的限制是否不适用于std::copy()?
另外,我刚刚在我的“placement new”答案中意识到,这让我想知道整个事情,我只是用了memcpy()代替std::memcpy(),而我没有, using namespace std;那么调用了哪个函数?是memcpy()与 不同的实现吗std::memcpy()?