我开发了自己的字符串类,它既有小字符串优化,又有内部标志,可以知道字符串是Ascii,UTF8,WTF8还是字节字符串。构造函数
String(const char* );
可用于构造Ascii字符串或UTF8字符串。它只应与文字一起使用,例如:
const String last_name = "Fayard"
const String first_name = "François"
构造函数需要计算字符串的长度并检查它是否为Ascii或UTF8。因此,我编写了这些函数,以便在编译时对它们进行评估。
inline constexpr il::int_t size(const char* s) {
return (*s == '\0') ? 0 : (size(s + 1) + 1);
}
inline constexpr bool isAscii(const char* s) {
return (*s == '\0')
? true
: (((static_cast<unsigned char>(*s) & 0x80_uchar) ==
0x00_uchar) && isAscii(s + 1));
}
构造函数是这样写的,并且在标题中可用,因此可以内联。
String(const char* data) {
const int n = size(data);
const bool ascii = isAscii(data);
if (n <= max_small_string) {
...
} else {
data_ = malloc();
...
}
}
但是我无法设法获得函数大小并且在编译时评估isAscii运行(尝试使用gcc 4.8.5,clang 4.0.1,icpc 17.0.4检查程序集)。有没有办法做到这一点?
PS:解决方案只需要是C ++ 11,并使用gcc 4.8.5和Visual Studio 2015进行编译。
答案 0 :(得分:2)
您可以使用enable_if来限制构造函数,使其不超过22个字符:
template<size_t N, typename std::enable_if<(N <= 22), int>::type = 0>
constexpr String(const char(&string_literal)[N]) { /*...*/ }
答案 1 :(得分:2)
函数的参数不是constexpr,因此你不能传播字符串文字。
一种方法是将文字字符串转换为字符序列:
template<typename C, C...cs> struct Chars
{
using str_type = C[1 + sizeof...(cs)];
static constexpr C str[1 + sizeof...(cs)] = {cs..., 0};
constexpr operator const str_type&() const { return str; }
};
template<typename C, C...cs> constexpr C Chars<C, cs...>::str[1 + sizeof...(cs)];
// Requires GNU-extension
template <typename C, C...cs>
constexpr Chars<C, cs...> operator""_cs() { return {}; }
如果没有gnu扩展名,你必须使用一些MACRO将文字转换为字符序列,就像我there一样。
然后您拥有类型的所有价值信息:
template <typename C, C ... Cs>
constexpr il::int_t size(Chars<C, Cs...>) {
return sizeof...(Cs);
}
template <typename C, C ... Cs>
constexpr bool isAscii(Chars<C, Cs...>) {
// C++17 folding expression
return ((static_cast<unsigned char>(Cs) & 0x80_uchar) == 0x00_uchar && ...);
}
或C ++ 11:
template <typename C>
constexpr bool isAscii(Chars<C>) { return true; }
template <typename C, C head, C ... Cs>
constexpr bool isAscii(Chars<C, Cs...>) {
// C++17 folding expression
return ((static_cast<unsigned char>(Head) & 0x80_uchar) == 0x00_uchar
&& isAscii(Chars<C, Cs...>{});
}
答案 2 :(得分:1)
这基本上是@ÖöTiib的ideea,但扩展为显示它在gcc 4.8.5
中工作,如您所说:
struct String
{
static const int max_small_string = 10;
int size_;
char* data_;
template <int N,
typename std::enable_if<(N <= String::max_small_string), void*>::type = nullptr>
constexpr String(const char (&str)[N])
: size_{size(str)},
data_{}
{
}
template <int N,
typename std::enable_if<(N > String::max_small_string), void*>::type = nullptr>
String(const char (&str)[N])
: size_{size(str)},
data_{static_cast<char*>(malloc(size_))}
{
}
};
auto foo() -> void
{
constexpr String ss = String{"asd"}; // OK, constexpr
String hs = String{"a sdjwq niornyuqe rniehr iwhtR Trtj rjtsd asde"};
}
你不能拥有一个包含malloc
的constexpr,没有办法解决它,它看起来永远不会。我读到了关于在标准中引入constexpr_vector
的一些讨论,但是在constexpr上下文中允许随机内存访问将非常棘手,因为constexpr需要在每个可能的UB上检测并失败,因此很可能不支持它在可预见的未来。
但是你可以有一个constexpr
小字符串构造函数,就像我给你看的那样。在godbolt with gcc 4.8.5
您说要初始化堆栈变量。我认为你的意思是自动存储。是的,可以在C ++ 11中完成:
template <int... Is> struct Seq{};
template <int I, int Max, int... Is>
struct Make_seq_impl
{
using Type = typename Make_seq_impl<I + 1, Max, Is..., I>::Type;
};
template <int Max, int... Is>
struct Make_seq_impl<Max, Max, Is...>
{
using Type = Seq<Is...>;
};
template <int N>
using Make_seq = typename Make_seq_impl<0, N>::Type;
struct X
{
static const int max_size_ = 10;
char data_[max_size_];
template <int N, int... Is>
constexpr X(const char (&str)[N], Seq<Is...>)
: data_ {(Is < N ? str[Is] : '\0')...}
{
}
template <int N>
constexpr X(const char (&str)[N])
: X(str, Make_seq<max_size_>{})
{
}
};
auto test() -> void
{
constexpr X x{"Asd"};
static_assert(x.data_[0] == 'A', "");
static_assert(x.data_[1] == 's', "");
static_assert(x.data_[2] == 'd', "");
static_assert(x.data_[3] == '\0', "");
}
我把它留给你结合两种方法。