c++ 如果clang 16不满足类的赋值运算符上的约束,则类不能trivially_copyable

zazmityj  于 2023-06-25  发布在  其他
关注(0)|答案(1)|浏览(130)

示例代码如下或godbolt。clang 16/trunk认为S<int>不是trivially_copyable类。clang 15、gcc trunk和MSVC则不这么认为。

  1. #include <type_traits>
  2. template<typename T>
  3. struct S {
  4. T m_t;
  5. S(S const&) = default;
  6. S(S&&) = default;
  7. S& operator=(S const&) requires (!std::is_integral<T>::value) = default;
  8. ~S() = default;
  9. };
  10. // next five assertions pass for all compilers
  11. static_assert(std::is_trivially_destructible<S<int>>::value);
  12. static_assert(std::is_trivially_copy_constructible<S<int>>::value);
  13. static_assert(std::is_trivially_move_constructible<S<int>>::value);
  14. static_assert(!std::is_copy_assignable<S<int>>::value);
  15. static_assert(!std::is_move_assignable<S<int>>::value);
  16. // compiles with gcc trunk, MSVC and clang 15, fails with clang 16/trunk
  17. static_assert(std::is_trivially_copyable<S<int>>::value);

根据标准的class.prop:
普通可复制类是一个类:

  • 具有至少一个合格的复制构造函数、移动构造函数、复制赋值运算符或移动赋值运算符([special],

[class.copy.ctor],[class.copy.assign]),

  • 其中每个合格的复制构造函数、移动构造函数、复制赋值运算符和移动赋值运算符都是平凡的,并且
  • 它有一个平凡的、未删除的析构函数([class.dtor])。

S<int>有平凡的复制/移动构造函数和平凡的析构函数。其复制/移动分配运算符不合格。我同意gcc/MSVC/clang 15的观点。是clang 16/trunk在这一个上错了还是我错过了什么?
编辑:这是一个确认的clang bug

93ze6v8z

93ze6v8z1#

是的,S<int>是可复制的。看起来你发现了一个叮当虫。我无法找到它在:

这可能是一个新的回归。

S<int>可平凡复制的证明

值得一提的是,* elecible * 是什么意思:
合格的特殊成员函数是满足以下条件的特殊成员函数:

  • 该函数不被删除,
    *相关的约束([temp.constr])(如果有的话)被满足,并且
  • 同类的特殊成员函数没有比它更受约束的了([temp.constr.order])。
  • [class.prop] §1
  1. S& operator=(S const&) requires (!std::is_integral<T>::value) = default

此复制赋值运算符是用户声明的(而不是用户提供的),并且不满足其约束,因此它是不合格的。此外:
如果类X的定义没有显式声明移动赋值运算符,则当且仅当

  • X没有用户声明的复制构造函数,
  • [...]
  • [class.copy.assign] $4
    这意味着:
  • 复制赋值运算符是用户声明的,但不符合条件
  • 未声明移动赋值运算符

所有剩余的(合格的)特殊成员函数都是平凡的,因此S<int>是平凡可复制的。

展开查看全部

相关问题