为何含std::array成员的类默认太空船运算符被删除?
类成员含std::array时默认operator<=>被隐式删除的问题分析
问题重现
以下代码在Apple Clang 14.x、Clang 15.x中无法编译:
#include <array> #include <compare> struct test { std::array<std::int64_t, 1> coords; auto operator<=>(const test&) const = default; }; void test_f() { test c1{1}, c2{2}; auto t1 = (c1 <= c2); }
编译错误提示:
defaulted 'operator<=>' is implicitly deleted because there is no viable
three-way comparison function for member 'coords'
但单独使用std::array的比较逻辑却能正常通过编译:
void test_2() { std::array<std::int64_t, 1> coords{0}; auto t1 = (coords <= coords); }
问题原因
这是Clang对C++20标准中条件性生成成员函数的处理逻辑导致的:
std::array的operator<=>是模板条件性提供的:只有当元素类型满足std::three_way_comparable要求时,才会实例化出这个三路比较运算符。- 编译器尝试为
test类默认生成operator<=>时,会检查成员coords是否存在可访问、非删除的三路比较运算符。但此时std::array<std::int64_t,1>的operator<=>还未被实例化,Clang会判定该成员没有符合要求的比较函数,因此将类的默认operator<=>标记为删除。 - 单独使用
std::array的比较时,编译器会触发模板实例化流程,此时元素类型std::int64_t支持三路比较,operator<=>会被正确生成,因此代码可以正常编译。
解决方案
方案1:手动实现类的operator<=>
绕过默认生成逻辑,直接调用std::array的比较运算符:
struct test { std::array<std::int64_t, 1> coords; auto operator<=>(const test& other) const { return coords <=> other.coords; } };
这种方式会强制编译器实例化std::array的operator<=>,确保比较逻辑正常生效。
方案2:显式指定比较结果类型
如果需要保留默认生成的语义,可显式指定比较结果类型,帮助Clang正确推导:
struct test { std::array<std::int64_t, 1> coords; std::strong_ordering operator<=>(const test&) const = default; };
注意这种方式依赖于元素类型的比较结果类型匹配,对于std::int64_t来说,std::strong_ordering是正确的选择。
补充说明
这是Clang在处理模板类条件性成员函数时的行为差异,GCC在相同场景下可以正常编译默认生成的operator<=>。如果需要兼容更多编译器,推荐使用方案1的手动实现方式。
内容的提问来源于stack exchange,提问作者Raffi
相关产品推荐
相关产品推荐

