【问题标题】:Passing a string literal to a template char array parameter将字符串文字传递给模板字符数组参数
【发布时间】:2021-09-02 13:34:39
【问题描述】:

CTRE library 能够在编译时使用ctre::match<"REGEX">(text_to_search) 等语法解析和验证正则表达式。我知道这种语法仅在 C++20 中受支持,这很好,但无论我尝试什么,我都无法以这种方式使用字符串文字。这是一个非常简单的例子:

// The compiler refuses to pass string literals to STR in this compile time version.
template <char const STR[2]> constexpr int to_int_compile_time()
{
    return STR[0] - '0';
}

// It has no problems passing the string literal to str in this version.
int to_int_runtime(char const str[2])
{
    return str[0] - '0';
}

调用to_int_runtime("0") 工作正常,但to_int_compile_time&lt;"0"&gt;() 抱怨字符串文字不能用于此模板参数。 to_int_compile_time应该怎么写,才能把字符串字面量传入char数组模板参数?

【问题讨论】:

    标签: c++ c++20 constexpr string-literals compile-time


    【解决方案1】:

    能够做到这一点取决于 C++20 的一个鲜为人知的特性:非类型模板参数可以具有类模板类型,无需指定模板参数。 CTAD 将决定这些论点。

    因此,您创建了一个以size_t N 为模板的类,该类具有char[N] 作为成员,可以从一个构造,并且N 可以由CTAD 推导出来。

    例子:

    // This does nothing, but causes an error when called from a `consteval` function.
    inline void expectedNullTerminatedArray() {}
    
    template <std::size_t N>
    struct ConstString
    {
        char str[N]{};
    
        static constexpr std::size_t size = N - 1;
    
        [[nodiscard]] std::string_view view() const
        {
            return {str, str + size};
        }
    
        consteval ConstString() {}
        consteval ConstString(const char (&new_str)[N])
        {
            if (new_str[N-1] != '\0')
                expectedNullTerminatedArray();
            std::copy_n(new_str, size, str);
        }
    };
    

    然后您执行template &lt;ConstString S&gt; struct A {...};,并使用S.strS.view() 来检查字符串。

    下面是这个类的一些额外的便利操作符:

    template <std::size_t A, std::size_t B>
    [[nodiscard]] constexpr ConstString<A + B - 1> operator+(const ConstString<A> &a, const ConstString<B> &b)
    {
        ConstString<A + B - 1> ret;
        std::copy_n(a.str, a.size, ret.str);
        std::copy_n(b.str, b.size, ret.str + a.size);
        return ret;
    }
    
    template <std::size_t A, std::size_t B>
    [[nodiscard]] constexpr ConstString<A + B - 1> operator+(const ConstString<A> &a, const char (&b)[B])
    {
        return a + ConstString<B>(b);
    }
    
    template <std::size_t A, std::size_t B>
    [[nodiscard]] constexpr ConstString<A + B - 1> operator+(const char (&a)[A], const ConstString<B> &b)
    {
        return ConstString<A>(a) + b;
    }
    

    您还可以使用此类具有模板 UDL:

    template <ConstString S>
    struct ConstStringParam {};
    
    template <ConstString S>
    [[nodiscard]] constexpr ConstStringParam<S> operator""_c()
    {
        return {};
    }
    
    // -----
    
    template <ConstString S> void foo(ConstStringParam<S>) {}
    
    foo("Sup!"_c);
    

    【讨论】:

    • 我尝试过做类似的事情,但我找不到在编译时表达式中访问字符串中字符的方法。例如,我将ASSERT(new_str[N-1] == '\0'); 替换为static_assert,因为这是我尝试使用的地方,并且编译器不认为new_str[N-1] 是一个有效的constexpr 表达式。
    • @BobBuilder 你不需要一个静态的。标准assert() 将起作用,因为在快乐的道路上它是 constexpr。我已经编辑了答案,以免混淆任何人。
    • 你是指assert() 来自assert.h 吗?我尝试使用那个,它编译但在编译时不检查任何内容。作为一个简单的测试,我输入了assert(new_str[0] == something_that_gives_a_false_result),它仍然编译没有错误。
    • @BobBuilder 是的,但我只是想出了一个更好的测试,请参阅编辑。
    • 谢谢,最后一次编辑实现了编译时字符串验证。感觉有点像 hack,但这是我见过的唯一真正解决问题的方法。 :)
    猜你喜欢
    • 2021-02-24
    • 2022-11-26
    • 1970-01-01
    • 2011-01-03
    • 1970-01-01
    • 2016-10-25
    • 1970-01-01
    • 2016-03-16
    • 1970-01-01
    相关资源
    最近更新 更多