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

small_vector::view的std::formatter特化编译失败的原因及解决方法

问题描述

我实现了一个模板类small_vector<typename T, size_t N>,其中T是值类型,N是内联桶大小,类内部定义了嵌套的view类:

template <typename T, size_t N> 
class small_vector {
   // ... 其他成员
   class view {
      // ... view的成员
   };
};

为了给view类定制std::format格式化器,我写了以下代码:

template<typename T, size_t N>
using small_vector_view = typename small_vector<T, N>::view;

template<typename T, size_t N> struct std::formatter<typename small_vector<T, N>::view, char> {
  enum class Style { Default, Compact, Pretty };
  Style style = Style::Default;

  constexpr auto parse(format_parse_context &ctx)
  {
    auto it = ctx.begin();
    if (it == ctx.end() || *it == '}')
      return it;

    switch (*it) {
      case 'c':
        style = Style::Compact;
        break;
      case 'p':
        style = Style::Pretty;
        break;
      default:
        throw format_error("invalid format specifier for small_vector");
    }
    return ++it;
  }

  auto format(const typename small_vector<T, N>::view &v, format_context &ctx) const
  {
    if (v.empty()) {
      if (style == Style::Pretty) {
        return format_to(ctx.out(), "[\n]");
      }
      return format_to(ctx.out(), "[]");
    }

    std::string result;
    if (style == Style::Pretty) {
      result = "[\n  ";
    }
    else {
      result = "[";
    }

    for (size_t i = 0; i < v.size(); ++i) {
      if (i > 0) {
        if (style == Style::Pretty) {
          result += ",\n  ";
        }
        else if (style == Style::Compact) {
          result += ",";
        }
        else {
          result += ", ";
        }
      }
      if constexpr (std::is_same_v<T, std::string>) {
        result += std::format("\"{}\"", v[i]);
      }
      else {
        result += std::format("{}", v[i]);
      }
    }

    if (style == Style::Pretty) {
      result += "\n]";
    }
    else {
      result += "]";
    }

    return format_to(ctx.out(), "{}", result);
  }
};

但代码无法编译,MSVC编译器报错:

D:\repos\ustring\small_vector.h(602): error C2764: 'T': template parameter not used or deducible in partial specialization 'std::formatter<small_vector<T,InlineCapacity>::view,char>'
D:\repos\ustring\small_vector.h(602): error C2764: 'N': template parameter not used or deducible in partial specialization 'std::formatter<small_vector<T,InlineCapacity>::view,char>'
D:\repos\ustring\small_vector_tests.cpp(557): error C7595: 'std::basic_format_string<char,small_vector<int,16>::view &>::basic_format_string': call to immediate function is not a constant expression

我想知道为什么编译器无法推导T和N,以及如何修复代码使其编译通过。


问题原因
  1. 模板参数推导失败:
    在偏特化std::formatter<typename small_vector<T, N>::view, char>中,typename small_vector<T, N>::view属于非推导上下文——编译器无法从view类型反向推导出外层small_vector的模板参数T和N。C++标准规定,嵌套依赖类型不能作为模板参数推导的依据。

  2. 常量表达式违反:
    C7595错误是因为parse函数中直接抛出format_error,而std::basic_format_string的构造是即时函数,要求parse在常量表达式上下文中不能抛出异常,必须使用格式化上下文提供的错误处理机制而非直接抛出异常。


修复方案

步骤1:给view类添加嵌套类型与常量,暴露T和N

修改small_vector的view定义,添加嵌套类型和静态常量,让外部能获取到对应的T和N:

template <typename T, size_t N> 
class small_vector {
public:
    class view {
    public:
        // 暴露外层模板参数
        using value_type = T;
        static constexpr size_t inline_capacity = N;
        // ... 原view的其他成员
    };
    // ... 原small_vector的其他成员
};

步骤2:使用Concept约束实现Formatter偏特化

定义一个Concept来识别small_vector::view类型,避免直接依赖外层模板参数推导:

#include <concepts>

template <typename View>
concept SmallVectorView = requires(View) {
    typename View::value_type;
    requires std::integral_constant<size_t, View::inline_capacity>::value;
    // 验证该View确实对应small_vector的view类型
    requires std::same_as<View, typename small_vector<typename View::value_type, View::inline_capacity>::view>;
};

然后基于这个Concept实现std::formatter的偏特化:

template <SmallVectorView View>
struct std::formatter<View, char> {
    using T = typename View::value_type;
    static constexpr size_t N = View::inline_capacity;

    enum class Style { Default, Compact, Pretty };
    Style style = Style::Default;

    constexpr auto parse(format_parse_context &ctx)
    {
        auto it = ctx.begin();
        if (it == ctx.end() || *it == '}')
            return it;

        switch (*it) {
            case 'c':
                style = Style::Compact;
                break;
            case 'p':
                style = Style::Pretty;
                break;
            default:
                // 使用ctx.on_error替代直接抛出异常,满足常量表达式要求
                ctx.on_error("invalid format specifier for small_vector view");
                return it;
        }
        return ++it;
    }

    auto format(const View &v, format_context &ctx) const
    {
        if (v.empty()) {
            if (style == Style::Pretty) {
                return format_to(ctx.out(), "[\n]");
            }
            return format_to(ctx.out(), "[]");
        }

        std::string result;
        if (style == Style::Pretty) {
            result = "[\n  ";
        }
        else {
            result = "[";
        }

        for (size_t i = 0; i < v.size(); ++i) {
            if (i > 0) {
                if (style == Style::Pretty) {
                    result += ",\n  ";
                }
                else if (style == Style::Compact) {
                    result += ",";
                }
                else {
                    result += ", ";
                }
            }
            if constexpr (std::is_same_v<T, std::string>) {
                result += std::format("\"{}\"", v[i]);
            }
            else {
                result += std::format("{}", v[i]);
            }
        }

        if (style == Style::Pretty) {
            result += "\n]";
        }
        else {
            result += "]";
        }

        return format_to(ctx.out(), "{}", result);
    }
};

步骤3:优化format函数(可选)

原format函数中先构建std::string再输出可以优化为直接写入ctx.out(),减少内存分配:

auto format(const View &v, format_context &ctx) const
{
    auto out = ctx.out();
    if (v.empty()) {
        if (style == Style::Pretty) {
            return format_to(out, "[\n]");
        }
        return format_to(out, "[]");
    }

    if (style == Style::Pretty) {
        out = format_to(out, "[\n  ");
    } else {
        out = format_to(out, "[");
    }

    for (size_t i = 0; i < v.size(); ++i) {
        if (i > 0) {
            if (style == Style::Pretty) {
                out = format_to(out, ",\n  ");
            } else if (style == Style::Compact) {
                out = format_to(out, ",");
            } else {
                out = format_to(out, ", ");
            }
        }
        if constexpr (std::is_same_v<T, std::string>) {
            out = format_to(out, "\"{}\"", v[i]);
        } else {
            out = format_to(out, "{}", v[i]);
        }
    }

    if (style == Style::Pretty) {
        out = format_to(out, "\n]");
    } else {
        out = format_to(out, "]");
    }

    return out;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 00:04:51