You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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;
}

错误原因与解决方法

初始代码错误原因

  1. 仅支持同类型比较:operator<=>的参数是const String&,仅能接受同模板参数N的实例,无法处理String<16>与String<32>的对比。
  2. 错误的比较语义:直接使用char数组的operator<=>是逐字节比较整个数组,而非以'\0'结尾的字符串语义比较,不符合字符串对比预期。
  3. 运算符合成问题:VS2022中,因返回值为数组比较的非标准类型,编译器无法正确合成==运算符。

修改模板运算符后的错误原因

  1. 返回值不符合标准:strcmp返回int,但C++20太空船运算符要求返回std::strong_ordering等标准比较类别类型,导致编译器重写比较表达式时产生歧义。
  2. const属性不匹配:operator<=>未声明为const,而operator==是const成员函数,内部调用operator<=>(str)时会因const对象无法调用非const函数而出错。
  3. 同类型比较歧义:对于同类型实例,编译器生成的两种重写表达式(0 < (a <=> b)和(b <=> a) < 0)因返回int而产生二义性。

改用std::array后的错误原因

  1. 比较语义仍错误:std::array的operator<=>依然是逐字节比较整个容器,而非字符串语义。
  2. 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;
}

关键改进点

  1. const正确性:operator<=>声明为const,确保const实例可以调用。
  2. 标准返回类型:返回std::strong_ordering,符合C++20太空船运算符规范,避免歧义。
  3. 正确的比较语义:使用std::strcmp实现以'\0'结尾的字符串对比,而非容器逐字节比较。
  4. 自动合成运算符:编译器会根据operator<=>自动合成==、!=、<、<=、>、>=等所有比较运算符,无需手动实现。

内容的提问来源于stack exchange,提问作者Andreas0815

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 23:55:55