来自模板参数的字符串的 constexpr 长度
constexpr length of a string from template parameter
我正在尝试使用 C++11 获取作为模板参数传递的字符串的长度。以下是我目前的发现:
#include <iostream>
#include <cstring>
extern const char HELLO[] = "Hello World!!!";
template<const char _S[]>
constexpr size_t len1() { return sizeof(_S); }
template<const char _S[]>
constexpr size_t len2() { return std::strlen(_S); }
template<const char _S[], std::size_t _Sz=sizeof(_S)>
constexpr size_t len3() { return _Sz-1; }
template<unsigned int _N>
constexpr size_t len5(const char(&str)[_N])
{
return _N-1;
}
int main() {
enum {
l1 = len1<HELLO>(),
// l2 = len2<HELLO>() does not compile
l3 = len3<HELLO>(),
l4 = len3<HELLO, sizeof(HELLO)>(),
l5 = len5(HELLO),
};
std::cout << l1 << std::endl; // outputs 4
// std::cout << l2 << std::endl;
std::cout << l3 << std::endl; // outputs 3
std::cout << l4 << std::endl; // outputs 14
std::cout << l5 << std::endl; // outputs 14
return 0;
}
我对结果不是很惊讶,我知道在 len1() 和 len2() 的情况下数组的大小丢失了,尽管信息在编译时存在。
有没有办法将有关字符串大小的信息也传递给模板?类似于:
template<const char _S[unsigned int _N]>
constexpr size_t len6() { return _N-1; }
[根据上下文和意图进行编辑]
我放弃了在编译时连接一组字符串的尝试,所以我尝试在初始化时进行连接。写 a().b().c().str()
会输出 "abc"
而 a().b().str()
会输出 "ab"
使用模板,a().b()
创建了一个父类型 A
的 B
类型。 a().b().c()
创建一个类型 C
,其父类型 B
具有父类型 A
,等等
给定一个类型 B
和父 A
,这是一个独特的类型,它可以有自己的静态缓冲区来保存连接(这就是为什么 l5
不是'对我有好处)。然后我可以在静态缓冲区中连续地对每一个进行 strcpy。我不想使用动态分配的缓冲区,因为此时我的分配器不一定已配置。
缓冲区的大小应该足以容纳与 A
关联的字符串和与 B
关联的字符串,这就是我想要弄清楚的。如果我明确地将 sizeof() 作为额外的模板参数(如上面代码段中的 l4
所做的那样),我可以让它工作,但这会使整个代码难以阅读且使用起来很麻烦。
[编辑 2] 我标记了最有帮助的答案 - 但 Yakk 的答案在 gcc 上也很好,但它没有用 Visual Studio.
编译
我目前的理解是,我们不能依靠 const char []
和外部链接来提供它们的大小。它可能在本地工作(如果模板是在与符号相同的单元中编译的),但如果 const char[]
在要在多个地方使用的头文件中,它就不会工作。
所以我放弃了尝试从 const char* 模板参数中提取长度,并决定使用 l4
,其中 sizeof()
也提供给模板参数。
对于那些好奇整个事情结果如何的人,我在 ideone 上粘贴了一个完整的工作示例:http://ideone.com/A0JwO8
我现在可以编写 Path<A>::b::c::path()
并在初始化时在静态缓冲区中获取相应的 "b.c"
字符串。
constexpr std::size_t length( const char * str ) {
return (!str||!*str)?0:(1+length(str+1));
}
template<const char * String>
constexpr size_t len() { return length(String); }
extern constexpr const char HELLO[] = "Hello World!!!";
live example。 C++14 不需要递归。
在编译时连接字符串,
使用 gnu 扩展,你可以这样做:
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 {}; }
template <typename C, C...lhs, C...rhs>
constexpr Chars<C, lhs..., rhs...>
operator+(Chars<C, lhs...>, Chars<C, rhs...>) { return {}; }
随着使用
constexpr auto hi = "Hello"_cs + " world\n"_cs;
std::cout << hi;
没有 gnu 扩展,你必须使用一些 MACRO 将文字转换为字符序列,就像我所做的那样 there。
我正在尝试使用 C++11 获取作为模板参数传递的字符串的长度。以下是我目前的发现:
#include <iostream>
#include <cstring>
extern const char HELLO[] = "Hello World!!!";
template<const char _S[]>
constexpr size_t len1() { return sizeof(_S); }
template<const char _S[]>
constexpr size_t len2() { return std::strlen(_S); }
template<const char _S[], std::size_t _Sz=sizeof(_S)>
constexpr size_t len3() { return _Sz-1; }
template<unsigned int _N>
constexpr size_t len5(const char(&str)[_N])
{
return _N-1;
}
int main() {
enum {
l1 = len1<HELLO>(),
// l2 = len2<HELLO>() does not compile
l3 = len3<HELLO>(),
l4 = len3<HELLO, sizeof(HELLO)>(),
l5 = len5(HELLO),
};
std::cout << l1 << std::endl; // outputs 4
// std::cout << l2 << std::endl;
std::cout << l3 << std::endl; // outputs 3
std::cout << l4 << std::endl; // outputs 14
std::cout << l5 << std::endl; // outputs 14
return 0;
}
我对结果不是很惊讶,我知道在 len1() 和 len2() 的情况下数组的大小丢失了,尽管信息在编译时存在。
有没有办法将有关字符串大小的信息也传递给模板?类似于:
template<const char _S[unsigned int _N]>
constexpr size_t len6() { return _N-1; }
[根据上下文和意图进行编辑]
我放弃了在编译时连接一组字符串的尝试,所以我尝试在初始化时进行连接。写 a().b().c().str()
会输出 "abc"
而 a().b().str()
会输出 "ab"
使用模板,a().b()
创建了一个父类型 A
的 B
类型。 a().b().c()
创建一个类型 C
,其父类型 B
具有父类型 A
,等等
给定一个类型 B
和父 A
,这是一个独特的类型,它可以有自己的静态缓冲区来保存连接(这就是为什么 l5
不是'对我有好处)。然后我可以在静态缓冲区中连续地对每一个进行 strcpy。我不想使用动态分配的缓冲区,因为此时我的分配器不一定已配置。
缓冲区的大小应该足以容纳与 A
关联的字符串和与 B
关联的字符串,这就是我想要弄清楚的。如果我明确地将 sizeof() 作为额外的模板参数(如上面代码段中的 l4
所做的那样),我可以让它工作,但这会使整个代码难以阅读且使用起来很麻烦。
[编辑 2] 我标记了最有帮助的答案 - 但 Yakk 的答案在 gcc 上也很好,但它没有用 Visual Studio.
编译我目前的理解是,我们不能依靠 const char []
和外部链接来提供它们的大小。它可能在本地工作(如果模板是在与符号相同的单元中编译的),但如果 const char[]
在要在多个地方使用的头文件中,它就不会工作。
所以我放弃了尝试从 const char* 模板参数中提取长度,并决定使用 l4
,其中 sizeof()
也提供给模板参数。
对于那些好奇整个事情结果如何的人,我在 ideone 上粘贴了一个完整的工作示例:http://ideone.com/A0JwO8
我现在可以编写 Path<A>::b::c::path()
并在初始化时在静态缓冲区中获取相应的 "b.c"
字符串。
constexpr std::size_t length( const char * str ) {
return (!str||!*str)?0:(1+length(str+1));
}
template<const char * String>
constexpr size_t len() { return length(String); }
extern constexpr const char HELLO[] = "Hello World!!!";
live example。 C++14 不需要递归。
在编译时连接字符串,
使用 gnu 扩展,你可以这样做:
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 {}; }
template <typename C, C...lhs, C...rhs>
constexpr Chars<C, lhs..., rhs...>
operator+(Chars<C, lhs...>, Chars<C, rhs...>) { return {}; }
随着使用
constexpr auto hi = "Hello"_cs + " world\n"_cs;
std::cout << hi;
没有 gnu 扩展,你必须使用一些 MACRO 将文字转换为字符序列,就像我所做的那样 there。