GCC与Clang对Wrapper<MoveOnly>的std::is_trivially_copyable判定分歧谁正确?
编译器分歧:
Wrapper<MoveOnly>是否属于平凡可拷贝类型? 首先定义仅可移动类型MoveOnly:
struct MoveOnly { MoveOnly() = default; ~MoveOnly() = default; MoveOnly(const MoveOnly&) = delete; MoveOnly& operator=(const MoveOnly&) = delete; MoveOnly(MoveOnly&&) noexcept = default; MoveOnly& operator=(MoveOnly&&) noexcept = default; };
GCC与Clang对该类型的特性判定完全一致:
static_assert(not std::is_trivially_copy_constructible_v<MoveOnly>); static_assert(not std::is_copy_constructible_v<MoveOnly>); static_assert( std::is_trivially_copyable_v<MoveOnly>);
接下来定义一个传播平凡拷贝构造性的包装器模板Wrapper:
template <typename T> struct Wrapper { Wrapper(const Wrapper&) requires(!std::is_trivially_copy_constructible_v<T> && std::is_copy_constructible_v<T>) { } Wrapper(const Wrapper&) requires(std::is_trivially_copy_constructible_v<T>) = default; };
对于Wrapper<MoveOnly>,GCC与Clang在以下两个断言上达成一致:
static_assert(not std::is_trivially_copy_constructible_v<Wrapper<MoveOnly>>); static_assert(not std::is_copy_constructible_v<Wrapper<MoveOnly>>);
但二者在平凡可拷贝特性的判定上出现分歧:
static_assert( std::is_trivially_copyable_v<Wrapper<MoveOnly>>); // GCC编译通过,Clang编译失败
正确判定:Clang的结果符合C++标准
核心依据:C++标准的特殊成员函数规则与平凡可拷贝定义
根据C++标准,一个类型要成为平凡可拷贝(trivially copyable),必须同时满足:
- 析构函数是平凡的;
- 至少存在一个非删除的拷贝构造函数、移动构造函数、拷贝赋值运算符或移动赋值运算符;
- 所有非删除的上述特殊成员函数都是平凡的。
针对Wrapper<MoveOnly>:
- 拷贝构造函数:用户声明的两个重载均因约束条件不满足被排除,因此
Wrapper<MoveOnly>的拷贝构造函数被视为删除状态。 - 移动构造/赋值、拷贝赋值运算符:由于用户显式声明了拷贝构造函数(即使是带约束的重载),编译器不会隐式生成这些特殊成员函数,它们同样被视为删除状态。
- 析构函数:未被用户声明,属于平凡析构,满足第一条要求,但第二条的必要条件(存在非删除的特殊成员函数)未被满足。
综上,Wrapper<MoveOnly>没有任何可用的非删除拷贝/移动构造或赋值运算符,不符合平凡可拷贝类型的定义,因此Clang的判定是正确的;GCC的错误在于,误将约束不满足的用户声明拷贝构造函数视为“未声明”,错误允许编译器隐式生成移动构造函数,进而错误判定其为平凡可拷贝类型。
内容的提问来源于stack exchange,提问作者Vittorio Romeo
相关产品推荐
相关产品推荐

