【问题标题】:constexpr length of a string from template parameter来自模板参数的字符串的 constexpr 长度
【发布时间】:2017-03-03 22:34:55
【问题描述】:

我正在尝试使用 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 类型,其父类型为 A。 a().b().c() 创建一个类型 C,其父类型为 B,其父类型为 A,等等。

给定一个类型 B 和一个父 A,这是一个独特的类型,它可以有自己的静态缓冲区来保存连接(这就是为什么 l5 不适合我)。然后我可以在静态缓冲区中连续地每一个 strcpy 。我不想使用动态分配的缓冲区,因为此时不必配置我的分配器。

该缓冲区的大小应该足够大以容纳与A 关联的字符串以及与B 关联的字符串是我想要弄清楚的。如果我明确地将 sizeof() 作为一个额外的模板参数(就像上面 sn-p 中的 l4 所做的那样),我可以让它工作,但这会使整个代码阅读起来很繁琐,使用起来也很麻烦。


[编辑 2] 我标记了最有帮助的答案 - 但 Yakk 的答案在 gcc 上也很好,但它没有用 Visual Studio 编译。

我在这一点上的理解是,我们不能依靠 const char [] 与外部链接来提供它们的大小。它可以在本地工作(如果模板与符号在同一单元中编译),但如果const char[] 在要在多个地方使用的头文件中,它将无法工作。

所以我放弃了尝试从 const char* 模板参数中提取长度,并决定使用l4,其中sizeof() 也提供给模板参数。

对于那些好奇整个事情结果如何的人,我在 ideone 上粘贴了一个完整的工作示例:http://ideone.com/A0JwO8

我现在可以在初始化时写入Path&lt;A&gt;::b::c::path() 并在静态缓冲区中获取相应的"b.c" 字符串。

【问题讨论】:

  • 你的len5怎么了?这不正是你想要的吗?
  • 你可以这样做:template&lt;typename T, const T&amp; value&gt; struct s; template&lt;typename T, std::size_t N, const T (&amp;value)[N]&gt; struct s&lt;const T[N], value&gt; s {/*..*/};(在c++17中用&lt;auto value&gt;简化)。
  • @SergeyA len5 确实给出了正确的结果,但它不是一种类型。这是对需要实际类型的更复杂问题的简化。
  • 你真正想要解决的问题是什么?
  • 动态分配缓冲区有什么问题(因此在编译时不关心其大小)?

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


【解决方案1】:
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 中不需要递归。

【讨论】:

  • 感谢您的建议 - 虽然您的建议确实会给我提供工作空间并且 gcc 似乎对此感到满意,但视觉工作室抱怨 extern constexpr const char HELLO[] 具有内部链接。如果我删除 constexpr,它具有外部链接,但是 length() 不再是 constexpr...
【解决方案2】:

在编译时连接字符串,
使用 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 所做的那样。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-03
    相关资源
    最近更新 更多