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

C++20及以下无宏实现超长字符串编译错误触发方案问询

嵌入式固定长度字符串的编译时长度校验实现

需求说明

需要实现适用于嵌入式场景的fixed_str固定长度字符串结构体,满足:

  • 运行时赋值超长字符串时自动静默截断(因显示设备尺寸限制,最终内容都会被裁剪)
  • 编译期通过以下方式赋值超长字符串时,触发编译错误:
    fixed_str<5> str = "bla bla";          // 超长,需编译报错
    fixed_str<5> str2 {"bla bla"};         // 超长,需编译报错
    fixed_str<5> str3 = std::string_view{"bla bla"};  // 超长,需编译报错
    fixed_str<5> str3 {std::string_view{"bla bla"}};   // 超长,需编译报错
    

现有实现问题

尝试的实现中,在operator=内通过std::is_constant_evaluated()判断编译期,再用static_assert校验长度,但函数参数无法作为static_assert的常量表达式参数,即使标记consteval重载也无效。同时不想使用宏(无法被命名空间包裹,易引发命名冲突)。

原有实现代码:

template<size_t length>
struct fixed_str final
{
  constexpr static auto max_length = length;
  static constexpr const char zero_char = 0;

  constexpr fixed_str()
  {
    chars[0] = 0;
  }

  constexpr fixed_str(const char* str)
  {
    this->operator=(str);
  }

  fixed_str& operator=(const char* str)
  {
    if (str)
    {
      auto len = std::char_traits<char>::length(str);
      auto real_len = std::min(len, length);
      if (std::is_constant_evaluated())
      {
        static_assert(len <= length, "Cannot fit the string into static buffer");
        for (size_t i = 0; i < real_len; ++i)
        {
          chars[i] = str[i];
        }
      }
      else
      {
        memcpy(chars.data(), str, real_len);
      }
      chars[real_len] = 0;
    }
    else
    {
      chars[0] = 0;
    }
    return *this;
  }

  std::array<char, length + 1> chars;

  constexpr operator std::string_view() const
  {
    return { chars.data() };
  }
};

解决方案

核心思路是区分编译期常量输入(字符串字面量、编译期已知的std::string_view)和运行时输入,针对不同场景实现重载:

1. 字符串字面量处理:模板构造/赋值

利用字符串字面量可推导模板参数的特性,直接在编译期获取长度并校验:

template<size_t length>
struct fixed_str final
{
  constexpr static auto max_length = length;
  std::array<char, length + 1> chars{};

  // 默认构造
  constexpr fixed_str() noexcept { chars[0] = '\0'; }

  // 字符串字面量构造:模板参数N为字面量总长度(含终止符)
  template<size_t N>
  constexpr fixed_str(const char (&str)[N]) noexcept
  {
    static_assert(N - 1 <= length, "String exceeds fixed_str capacity");
    const auto copy_len = std::min(N - 1, length);
    for (size_t i = 0; i < copy_len; ++i)
    {
      chars[i] = str[i];
    }
    chars[copy_len] = '\0';
  }

  // 字符串字面量赋值重载
  template<size_t N>
  constexpr fixed_str& operator=(const char (&str)[N]) noexcept
  {
    static_assert(N - 1 <= length, "String exceeds fixed_str capacity");
    const auto copy_len = std::min(N - 1, length);
    for (size_t i = 0; i < copy_len; ++i)
    {
      chars[i] = str[i];
    }
    chars[copy_len] = '\0';
    return *this;
  }

2. std::string_view编译期处理:consteval重载

用consteval强制要求输入为编译期常量,结合编译期长度校验:

// 编译期获取string_view长度(仅当size为常量表达式时通过)
  static constexpr size_t get_constant_size(std::string_view sv) noexcept
  {
    return sv.size();
  }

  // consteval构造:仅接受编译期已知的string_view
  consteval fixed_str(std::string_view sv) noexcept
  {
    static_assert(get_constant_size(sv) <= length, "String exceeds fixed_str capacity");
    const auto copy_len = std::min(sv.size(), length);
    for (size_t i = 0; i < copy_len; ++i)
    {
      chars[i] = sv[i];
    }
    chars[copy_len] = '\0';
  }

  // consteval赋值:仅接受编译期已知的string_view
  consteval fixed_str& operator=(std::string_view sv) noexcept
  {
    static_assert(get_constant_size(sv) <= length, "String exceeds fixed_str capacity");
    const auto copy_len = std::min(sv.size(), length);
    for (size_t i = 0; i < copy_len; ++i)
    {
      chars[i] = sv[i];
    }
    chars[copy_len] = '\0';
    return *this;
  }

3. 运行时输入处理:普通重载

针对运行时的const char*或std::string_view,实现自动截断逻辑:

// 运行时const char*构造
  constexpr fixed_str(const char* str) noexcept
  {
    if (!str)
    {
      chars[0] = '\0';
      return;
    }
    const auto len = std::char_traits<char>::length(str);
    const auto copy_len = std::min(len, length);
    for (size_t i = 0; i < copy_len; ++i)
    {
      chars[i] = str[i];
    }
    chars[copy_len] = '\0';
  }

  // 运行时const char*赋值
  constexpr fixed_str& operator=(const char* str) noexcept
  {
    if (!str)
    {
      chars[0] = '\0';
      return *this;
    }
    const auto len = std::char_traits<char>::length(str);
    const auto copy_len = std::min(len, length);
    if (std::is_constant_evaluated())
    {
      // 若str是编译期常量但走此重载,强制校验长度
      constexpr auto const_len = std::char_traits<char>::length(str);
      static_assert(const_len <= length, "String exceeds fixed_str capacity");
      for (size_t i = 0; i < copy_len; ++i)
      {
        chars[i] = str[i];
      }
    }
    else
    {
      memcpy(chars.data(), str, copy_len);
    }
    chars[copy_len] = '\0';
    return *this;
  }

  // 运行时string_view构造
  constexpr fixed_str(std::string_view sv) noexcept
  {
    const auto copy_len = std::min(sv.size(), length);
    for (size_t i = 0; i < copy_len; ++i)
    {
      chars[i] = sv[i];
    }
    chars[copy_len] = '\0';
  }

  // 运行时string_view赋值
  constexpr fixed_str& operator=(std::string_view sv) noexcept
  {
    const auto copy_len = std::min(sv.size(), length);
    if (std::is_constant_evaluated())
    {
      // 尝试编译期校验,仅当sv为常量表达式时触发
      if constexpr (requires { get_constant_size(sv); })
      {
        static_assert(get_constant_size(sv) <= length, "String exceeds fixed_str capacity");
      }
      for (size_t i = 0; i < copy_len; ++i)
      {
        chars[i] = sv[i];
      }
    }
    else
    {
      memcpy(chars.data(), sv.data(), copy_len);
    }
    chars[copy_len] = '\0';
    return *this;
  }

  // 转换为string_view
  constexpr operator std::string_view() const noexcept
  {
    return { chars.data() };
  }
};

关键说明

  • 字符串字面量通过模板参数推导长度,直接在static_assert中完成编译期校验
  • consteval版本的std::string_view重载强制要求输入为编译期常量,否则编译失败
  • 运行时版本自动截断超长内容,不做编译期校验
  • 完全基于C++20原生特性实现,避免了宏的使用

内容的提问来源于stack exchange,提问作者Tomáš Zato

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 09:54:51