constexpr模板参数的字符串长度

时间:2016-10-21 14:45:41

标签: c++ string c++11 templates string-length

我正在尝试使用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()会创建一种B类型,其父类型为Aa().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"字符串。

2 个答案:

答案 0 :(得分:3)

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中不需要递归。

答案 1 :(得分:1)

要在编译时连接字符串,
使用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;

Demo

如果没有gnu扩展名,你必须使用一些MACRO将文字转换为字符序列,就像我there一样。