英文:
Class isn't trivially_copyable if a constraint on its assignment operator is not satisfied with clang 16
问题
以下是翻译好的内容:
示例代码如下或在[godbolt][1]上查看。clang 16/trunk认为`S<int>`不是一个`trivially_copyable`类。而clang 15、gcc trunk和MSVC则持相反观点。
#include <type_traits>
template<typename T>
struct S {
T m_t;
S(S const&) = default;
S(S&&) = default;
S& operator=(S const&) requires (!std::is_integral<T>::value) = default;
~S() = default;
};
// 下面的五个断言对所有编译器都通过
static_assert(std::is_trivially_destructible<S<int>>::value);
static_assert(std::is_trivially_copy_constructible<S<int>>::value);
static_assert(std::is_trivially_move_constructible<S<int>>::value);
static_assert(!std::is_copy_assignable<S<int>>::value);
static_assert(!std::is_move_assignable<S<int>>::value);
// 在gcc trunk、MSVC和clang 15上编译通过,在clang 16/trunk上失败
static_assert(std::is_trivially_copyable<S<int>>::value);
根据标准[class.prop][2]:
> 一个trivially copyable类是一个类:
>
> - 至少具有一个合格的复制构造函数、移动构造函数、复制赋值运算符或移动赋值运算符([special]、[class.copy.ctor]、[class.copy.assign]),
>
> - 每个合格的复制构造函数、移动构造函数、复制赋值运算符和移动赋值运算符都是trivial的,且
>
> - 具有一个trivial、非删除的析构函数([class.dtor])。
`S<int>`具有trivial复制/移动构造函数和trivial析构函数。它的复制/移动赋值运算符不合格。我同意gcc/MSVC/clang15的观点。clang 16/trunk是否有错或我漏掉了什么?
编辑:这是一个已确认的[clang错误][3]。
[1]: https://godbolt.org/z/dzorGb68o
[2]: https://timsong-cpp.github.io/cppwp/n4868/class.prop#1
[3]: https://github.com/llvm/llvm-project/issues/63352
注意:我已经删除了代码部分,只提供了翻译的文本。如果您需要更多翻译,请提供具体的文本部分。
英文:
Example code as below or on godbolt. clang 16/trunk believes S<int>
is not a trivially_copyable
class. clang 15, gcc trunk and MSVC believe otherwise.
#include <type_traits>
template<typename T>
struct S {
T m_t;
S(S const&) = default;
S(S&&) = default;
S& operator=(S const&) requires (!std::is_integral<T>::value) = default;
~S() = default;
};
// next five assertions pass for all compilers
static_assert(std::is_trivially_destructible<S<int>>::value);
static_assert(std::is_trivially_copy_constructible<S<int>>::value);
static_assert(std::is_trivially_move_constructible<S<int>>::value);
static_assert(!std::is_copy_assignable<S<int>>::value);
static_assert(!std::is_move_assignable<S<int>>::value);
// compiles with gcc trunk, MSVC and clang 15, fails with clang 16/trunk
static_assert(std::is_trivially_copyable<S<int>>::value);
According to the standard class.prop:
> A trivially copyable class is a class:
>
> - that has at least one eligible copy constructor, move constructor, copy assignment operator, or move assignment operator ([special],
> [class.copy.ctor], [class.copy.assign]),
>
> - where each eligible copy constructor, move constructor, copy assignment operator, and move assignment operator is trivial, and
>
> - that has a trivial, non-deleted destructor ([class.dtor]).
S<int>
has trivial copy/move constructor and trivial destructor. Its copy/move assignment operators are not eligible. I would agree with gcc/MSVC/clang15 on this. Is clang 16/trunk wrong on this one or am I missing something?
Edit: This is a confirmed clang bug.
答案1
得分: 1
是的,S<int>
是可以轻松复制的。看起来你发现了一个 clang 的 bug。我在以下地方都找不到它:
所以这可能是一个新的退化情况。
S<int>
是可以轻松复制的证明
值得考虑 elegible 的含义:
> 一个符合条件的特殊成员函数是指满足以下条件的特殊成员函数:
> - 函数没有被删除,
> - **关联的约束([temp.constr]),如果有的话,被满足,
> - 同一种特殊成员函数没有更多的约束([temp.constr.order])。
S& operator=(S const&) requires (!std::is_integral<T>::value) = default
这个复制赋值操作符是用户声明的(而不是用户提供的),它的约束条件没有被满足,所以它不符合条件。此外:
> 如果类 X
的定义没有显式声明移动赋值操作符,那么只有在以下条件下才会隐式声明为默认值,即
> - X
没有用户声明的复制构造函数,
> - [...]
这意味着:
- 复制赋值操作符是用户声明的,但不符合条件
- 移动赋值操作符未声明
所有剩下的(符合条件的)特殊成员函数都是平凡的,因此 S<int>
是可以轻松复制的。
英文:
Yes, S<int>
is trivially copyable. It looks like you've discovered a clang bug. I was unable to find it in:
So this may be a new regression.
Proof for S<int>
being trivially copyable
It's worth examining what elegible means:
> An eligible special member function is a special member function for which:
> - the function is not deleted,
> - the associated constraints ([temp.constr]), if any, are satisfied, and
> - no special member function of the same kind is more constrained ([temp.constr.order]).
S& operator=(S const&) requires (!std::is_integral<T>::value) = default
This copy assignment operator is user-declared (and not user-provided) and its constraints aren't satisfied, so it is not eligible. Furthermore:
> If the definition of a class X
does not explicitly declare a move assignment operator, one will be implicitly declared as defaulted if and only if
> - X
does not have a user-declared copy constructor,
> - [...]
This means that:
- the copy-assignment operator is user-declared, but not eligible
- the move-assignment operator is not declared
All of the remaining (eligible) special member functions are trivial, therefore S<int>
is trivially copyable.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论