能够做到这一点取决于C++20中鲜为人知的一个特性:非类型模板参数可以具有类模板类型,而无需指定模板参数。CTAD将确定这些参数。
因此,您可以创建一个由size_t N
模板化的类,该类具有char[N]
作为成员,可以从一个构造,并且N
可以通过CTAD推导出来。
例如:
inline void expectedNullTerminatedArray() {}
template <std::size_t N>
struct ConstString
{
char str[N]{};
static constexpr std::size_t size = N - 1;
[[nodiscard]] constexpr 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 <ConstString S> struct A {...};
,并使用S.str
或S.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);
static_assert
替换了ASSERT(new_str[N-1] == '\0');
,因为这就是我想要的结果,但编译器不认为new_str[N-1]
是一个有效的constexpr表达式。 - Bob Builderassert()
会起作用,因为在正常情况下它是constexpr的。我已经编辑了答案,以免混淆任何人。 - HolyBlackCatassert.h
中的assert()
吗?我尝试使用了它,虽然编译通过了,但并没有在编译时检查任何内容。作为一个简单的测试,我输入了assert(new_str[0] == something_that_gives_a_false_result)
,但它仍然可以编译通过,没有出现错误。 - Bob Builder