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

C++模板参数推导问题:C字符串字面量无法转为std::string_view

解决C++20 PEG解析器中lit_模板无法推导C字符串字面量的问题

问题核心是当传入C字符串字面量(如"world")时,模板参数V会被推导为const char[6],而非预期的std::string_view,导致与现有Viewer体系不兼容。以下是两种可行的解决方案:

方案1:使用类模板推导指引(CTAD)+ 模板构造函数

通过添加推导指引告诉编译器如何将原始输入类型映射到目标V类型,同时修改构造函数支持隐式转换:

#include <span>
#include <string_view>
#include <vector>

struct Token
{
};

template <typename T>
struct Viewer;

template <>
struct Viewer<Token>
{
    using type = std::span<Token>;
};

template <>
struct Viewer<char>
{
    using type = std::string_view;
};

template <typename T> using ViewerT = typename Viewer<T>::type;

template <typename Base, typename T>
struct parser_base {
    using v = ViewerT<T>;
    using charType = T;
};

template<typename V>
struct lit_ final : public parser_base<lit_<V>, typename V::value_type> {
    // 模板构造函数:接受可转换为V的任意类型
    template <typename U>
    constexpr lit_(U&& str) noexcept
        : str(std::forward<U>(str))
    {}

private:
    V str;
};

// 推导指引:C字符串字面量 → lit_<std::string_view>
template <size_t N>
lit_(const char (&)[N]) -> lit_<std::string_view>;

// 推导指引:const char* → lit_<std::string_view>
lit_(const char*) -> lit_<std::string_view>;

// 推导指引:Token数组 → lit_<std::span<Token>>
template <size_t N>
lit_(Token (&)[N]) -> lit_<std::span<Token>>;

// 可选:支持直接传入std::vector<Token>
lit_(std::vector<Token>&) -> lit_<std::span<Token>>;

int main()
{
    auto p1 = lit_("world");  // 现在可正常编译
    auto p2 = lit_(std::string_view("world"));

    Token a;
    std::vector<Token> tokens;
    tokens.push_back(a);
    tokens.push_back(a);
    tokens.push_back(a);
    std::span<Token> match(tokens.begin(), tokens.size());
    auto p3 = lit_(match);

    Token arr[3] = {};
    auto p4 = lit_(arr);  // 自动推导为lit_<std::span<Token>>
    return 0;
}

原理说明

  • 模板构造函数允许接受任意可隐式转换为V的类型,比如const char[]可以转换为std::string_view。
  • 推导指引明确告诉编译器,当传入C字符串、Token数组等类型时,直接推导V为对应的std::string_view或std::span<Token>,跳过原始数组类型的推导。

方案2:使用工厂函数封装类型推导

如果希望用户调用更简洁,可以定义一个工厂函数lit,自动完成类型映射:

#include <span>
#include <string_view>
#include <vector>
#include <type_traits>

struct Token
{
};

template <typename T>
struct Viewer;

template <>
struct Viewer<Token>
{
    using type = std::span<Token>;
};

template <>
struct Viewer<char>
{
    using type = std::string_view;
};

template <typename T> using ViewerT = typename Viewer<T>::type;

template <typename Base, typename T>
struct parser_base {
    using v = ViewerT<T>;
    using charType = T;
};

template<typename V>
struct lit_ final : public parser_base<lit_<V>, typename V::value_type> {
    constexpr lit_(V str) noexcept
        : str(str)
    {}

private:
    V str;
};

// 工厂函数:自动推导目标V类型
template <typename U>
constexpr auto lit(U&& str) {
    using RawType = std::remove_cvref_t<U>;
    if constexpr (std::is_array_v<RawType> || std::is_same_v<RawType, const char*>) {
        return lit_<std::string_view>(std::forward<U>(str));
    } else if constexpr (std::is_convertible_v<U, std::span<Token>>) {
        return lit_<std::span<Token>>(std::forward<U>(str));
    } else {
        // 回退到直接推导原始类型
        return lit_(std::forward<U>(str));
    }
}

int main()
{
    auto p1 = lit("world");  // 自动生成lit_<std::string_view>
    auto p2 = lit(std::string_view("world"));

    Token a;
    std::vector<Token> tokens;
    tokens.push_back(a);
    tokens.push_back(a);
    tokens.push_back(a);
    std::span<Token> match(tokens.begin(), tokens.size());
    auto p3 = lit(match);
    return 0;
}

原理说明

  • 工厂函数通过constexpr if分支判断输入类型,将C字符串、Token数组等自动转换为对应的lit_实例,用户无需手动指定模板参数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 04:45:00