如何为C++模板类定义太空船运算符?支持跨大小字符串对比
C++模板字符串类太空船运算符实现问题解析
问题背景
需要为模板字符串类String<N>实现太空船运算符operator<=>,支持不同模板参数的实例对比(如String<16> < String<32>),使用Visual Studio 2022 C++20环境,过程中遇到多次编译错误。
初始代码与错误
初始代码
namespace mylib { template <size_t N> struct String { String() { *data = '\0'; } String(const char *str) { size_t l = strlen(str); if (l >= N) throw std::runtime_error(str); memcpy(data, str, l + 1); } auto operator<=>(const String& str) const { return data <=> str.data; } char data[N]; }; } void test_string() { mylib::String<16> str1; mylib::String<16> str2("Hallo"); mylib::String<16> str3 = "Hallo"; mylib::String<16> str4 = "hallo"; assert(str2 == str3); assert(str4 != str2); assert(str2 < str4); assert(str2 <= str4); assert(str4 > str2); assert(str4 >= str2); mylib::String<3> str5 = "Hallo"; }
编译错误
1>E:\Projects\Windows\ind\IndDlg.cpp(104,2): error C2678: binary '==': no operator found which takes a left-hand operand of type 'mylib::String<16>' (or there is no acceptable conversion) 1> 'bool operator ==(const D2D1_SIZE_U &,const D2D1_SIZE_U &)': cannot convert argument 1 from 'mylib::String<16>' to 'const D2D1_SIZE_U &'
修改模板运算符后的代码与错误
修改后代码
// String.cpp #include <cassert> #include <compare> #include <stdexcept> namespace mylib { template <size_t N> struct String { String() { *data = '\0'; } String(const char* str) { size_t l = strlen(str); if (l >= N) throw std::runtime_error(str); memcpy(data, str, l + 1); } template<size_t N2> auto operator<=>(const String<N2>& str) { return strcmp(data, str.data); } template<size_t N2> bool operator==(const String<N2>& str) const { return operator<=>(str) == 0; } char data[N]; }; } // namespace mylib int main() { mylib::String<16> str1; mylib::String<16> str2("Hallo"); mylib::String<162> str3 = "Hallo"; mylib::String<16> str4 = "hallo"; assert(str2 == str3); assert(str4 != str2); assert(str2 < str4); assert(str2 <= str4); assert(str4 > str2); assert(str4 >= str2); mylib::String<3> str5 = "Hallo"; return 0; }
编译错误
1>E:\Projects\WindowsTest\String\String.cpp(53,2): error C2666: 'mylib::String<16>::operator <=>': overloaded functions have similar conversions 1> E:\Projects\WindowsTest\String\String.cpp(29,8): 1> could be 'auto mylib::String<16>::operator <=><16>(const mylib::String<16> &)' [rewritten expression '0 < (x <=> y)'] 1> E:\Projects\WindowsTest\String\String.cpp(29,8): 1> or 'auto mylib::String<16>::operator <=><16>(const mylib::String<16> &)' [synthesized expression '(y <=> x) < 0'] 1> E:\Projects\WindowsTest\String\String.cpp(53,2): 1> while trying to match the argument list '(mylib::String<16>, mylib::String<16>)'
改用std::array后的代码与错误
最终修改代码
// String.cpp #include <array> #include <cassert> #include <compare> #include <stdexcept> namespace mylib { // This class is for short constant strings on the stack without any overhead. template <size_t N> struct String { String() { *data = '\0'; } String(const char* str) { size_t l = strlen(str); if (l >= N) throw std::runtime_error(str); memcpy(data, str, l + 1); } template<size_t N2> auto operator<=>(const String<N2>& str) { return data <=> str.data; } template<size_t N2> bool operator==(const String<N2>& str) const { return (data <=> str.data) == 0; } std::array<char, N> data; }; } // namespace mylib int main() { mylib::String<16> str1; mylib::String<16> str2("Hallo"); mylib::String<32> str3 = "Hallo"; mylib::String<32> str4 = "hallo"; assert(str2 == str3); assert(str4 != str2); assert(str2 < str4); assert(str2 <= str4); assert(str4 > str2); assert(str4 >= str2); mylib::String<3> str5 = "Hallo"; return 0; }
错误原因与解决方法
初始代码错误原因
- 仅支持同类型比较:
operator<=>的参数是const String&,仅能接受同模板参数N的实例,无法处理String<16>与String<32>的对比。 - 错误的比较语义:直接使用char数组的
operator<=>是逐字节比较整个数组,而非以'\0'结尾的字符串语义比较,不符合字符串对比预期。 - 运算符合成问题:VS2022中,因返回值为数组比较的非标准类型,编译器无法正确合成
==运算符。
修改模板运算符后的错误原因
- 返回值不符合标准:
strcmp返回int,但C++20太空船运算符要求返回std::strong_ordering等标准比较类别类型,导致编译器重写比较表达式时产生歧义。 - const属性不匹配:
operator<=>未声明为const,而operator==是const成员函数,内部调用operator<=>(str)时会因const对象无法调用非const函数而出错。 - 同类型比较歧义:对于同类型实例,编译器生成的两种重写表达式(
0 < (a <=> b)和(b <=> a) < 0)因返回int而产生二义性。
改用std::array后的错误原因
- 比较语义仍错误:
std::array的operator<=>依然是逐字节比较整个容器,而非字符串语义。 - const属性问题未解决:
operator<=>仍未声明为const,const实例无法调用。
正确实现方案
#include <array> #include <cassert> #include <compare> #include <cstring> #include <stdexcept> namespace mylib { template <size_t N> struct String { String() { data[0] = '\0'; } String(const char* str) { const size_t l = std::strlen(str); if (l >= N) throw std::runtime_error("字符串过长"); std::memcpy(data.data(), str, l + 1); } // 支持不同模板参数的实例对比,声明为const template <size_t N2> std::strong_ordering operator<=>(const String<N2>& str) const { const int cmp = std::strcmp(data.data(), str.data.data()); return cmp <=> 0; // 将int结果转换为标准比较类别 } std::array<char, N> data; }; } int main() { mylib::String<16> str1; mylib::String<16> str2("Hallo"); mylib::String<32> str3 = "Hallo"; mylib::String<32> str4 = "hallo"; assert(str2 == str3); assert(str4 != str2); assert(str2 < str4); assert(str2 <= str4); assert(str4 > str2); assert(str4 >= str2); // 以下代码会抛出异常,符合预期 // mylib::String<3> str5 = "Hallo"; return 0; }
关键改进点
- const正确性:
operator<=>声明为const,确保const实例可以调用。 - 标准返回类型:返回
std::strong_ordering,符合C++20太空船运算符规范,避免歧义。 - 正确的比较语义:使用
std::strcmp实现以'\0'结尾的字符串对比,而非容器逐字节比较。 - 自动合成运算符:编译器会根据
operator<=>自动合成==、!=、<、<=、>、>=等所有比较运算符,无需手动实现。
内容的提问来源于stack exchange,提问作者Andreas0815
相关产品推荐
相关产品推荐

