【问题标题】:Why is the copy constructor not trivial just because there is a user defined destructor? [duplicate]为什么复制构造函数不是微不足道的,只是因为有一个用户定义的析构函数? [复制]
【发布时间】:2019-06-06 09:03:29
【问题描述】:

以下摘录在 Clang-libstdc++ 或 Clang-libc++、GCC、它们的许多版本以及自 11(14 和 17)以来的所有三个版本的语言中编译:

#include <type_traits>

struct HasUserDefinedDestructor {
    ~HasUserDefinedDestructor() {}
};

using HUDD = HasUserDefinedDestructor;

static_assert(not std::is_trivially_move_constructible<HUDD>::value, "");
static_assert(not std::is_trivially_copy_constructible<HUDD>::value, "");

这让我很吃惊,因为副本只需要一些琐碎的操作。

这是编译器/库中的错误,还是标准在某处说拥有用户定义的析构函数会使复制和移动构造函数变得不简单?

编辑:为什么这不是默认构造问题的重复: 鉴于 cmets,我们知道构造函数的“无异常”和琐碎性受到析构函数的无异常性和琐碎性的影响,但在知道所有这些特征都相关之前,问题是不同的。有这个问题可以让任何人看到这是相关的

【问题讨论】:

  • 如果你使用= default作为你的析构函数,你会得到不同的结果。
  • "有这个问题可以让任何人看到这是相关的" 如果它是重复的,这个问题仍然会在这里。它只会指向另一个问题,表明它们是相关的。

标签: c++ language-lawyer template-meta-programming typetraits


【解决方案1】:

is_trivially_constructibledefined as follows(粗体是我的):

is_­constructible_­v&lt;T,Args...&gt;trueis_­constructible 的变量定义,定义如下,已知不会调用不重要的操作

«定义如下»是[meta.unary.op]/8:

模板特化is_­constructible&lt;T, Args...&gt;的谓词条件当且仅当以下变量定义对于某个发明变量t是良构的:

T t(declval&lt;Args&gt;()...);

所以是的,is_trivially_[copy|move]_constructible_vfalse 当析构函数不是微不足道的。

【讨论】:

  • 即变量t的定义,t没有被销毁。分号有所不同:它不是创建和销毁T 的临时表达式,它只是创建一个未在此处销毁的变量t
  • 由于最后一个链接(很好的发现),我们可以得出结论,这些实现被破坏了,因为他们考虑的不是S t();,而是S{},一个创建和销毁的表达式。
  • @TheCppZoo 我想说这是措辞上的问题。应该说,已知的变量定义不会导致对操作的调用不是微不足道的。
【解决方案2】:

[dcl.fct.def.default]/5:

[...] 一个函数是用户提供的,如果它是用户声明的而不是显式的 在第一次声明时违约或删除。 [...]

因此,给定

struct HasUserDefinedDestructor {
    ~HasUserDefinedDestructor() {}
};

HasUserDefinedDestructor 有一个用户提供的析构函数。

[class.dtor]/6

如果析构函数不是用户提供的并且如果:[...]

否则,析构函数是非平凡的

因此,HasUserDefinedDestructor 有一个 非平凡 析构函数。

[meta.unary.prop]

template <class T, class... Args>
struct is_­trivially_constructible;

条件: is_­constructible_­v&lt;T, Args...&gt;trueis_­constructible 的变量定义,定义如下,是 已知不会调用任何重要的操作([basic.types], [特殊])。

前置条件:T和参数包Args中的所有类型都应该是一个完整的类型,cvvoid,或者一个未知的数组绑定。

template <class T>
struct is_­trivially_copy_­constructible;

条件:对于可引用类型T,结果与is_­trivially_­constructible_­v&lt;T, const T&amp;&gt;相同,否则为false

前置条件: T 应该是一个完整的类型,cv void,或者一个未知边界的数组。

template <class T>
struct is_­trivially_­move_­constructible;

条件:对于可引用类型T,结果与is_­trivially_­constructible_­v&lt;T, T&amp;&amp;&gt;相同,否则为false

前置条件: T 应该是一个完整的类型,cv void,或者一个未知边界的数组。

[meta.unary.prop]/8

模板特化的谓词条件 当且仅当满足is_­constructible&lt;T, Args...&gt; 对于某些发明,以下变量定义将是格式良好的 变量t:

T t(declval<Args>()...);

[ 注意: 这些标记永远不会被解释为函数 宣言。 — 尾注 ] 访问检查的执行就像 在与T 和任何Args 无关的上下文中。只有有效期 考虑变量初始化的直接上下文。 [ 注意:初始化的评估可能会导致边 类模板特化的实例化等效果 和功能模板特化,生成 隐式定义的函数,等等。这种副作用不在 “直接上下文”,并可能导致程序被 格式不正确。 — 尾注 ]

变量定义应该“调用”析构函数,即使析构函数似乎没有在定义的位置被调用。因此,std::is_trivially_move_constructible&lt;HUDD&gt;::valuefalsestd::is_trivially_copy_constructible&lt;HUDD&gt;::value 也是。

【讨论】:

  • 变量定义t和析构函数之间似乎没有关系。在T t(declval&lt;const T &amp;&gt;()) 中没有执行析构函数,那么,驱动移动的非平凡操作是什么,复制构造函数非平凡?
  • @TheCppZoo 变量定义应该“调用”析构函数。
  • 即变量t的定义,在一个语句中(有分号),在那个语句中没有调用t的析构函数。那是用于指定决定琐碎性所涉及的操作的语句,因此,您的答案根本不是原因
  • @TheCppZoo 该变量定义导致调用 S 析构函数。 cplusplus.github.io/LWG/issue2827
  • @Peter 如果 T 是类/结构的成员怎么办 那么它不是变量定义。
猜你喜欢
  • 2012-02-28
  • 2015-06-10
  • 2012-06-28
  • 1970-01-01
  • 1970-01-01
  • 2012-04-29
  • 2017-07-21
  • 1970-01-01
相关资源
最近更新 更多