【问题标题】:Convert a number to a string literal with constexpr [duplicate]使用 constexpr 将数字转换为字符串文字 [重复]
【发布时间】:2014-06-02 16:53:18
【问题描述】:

我正在寻找一种在编译时将数字转换为字符串文字的方法。它应该看起来像这样:

template <unsigned num>
struct num_to_string {
    constexpr static char value[] = /* ... magic goes here ... */;
};

所以num_to_string&lt;5&gt;::value 等于"5"{'5', '\0'}

这对于在编译时从某个数字生成字符串很有用,该数字是其他一些 constexpr 数字计算的结果。

另外请注意,我只对unsigned 数字感兴趣,因为这应该更容易处理。签名版的奖励积分:)

编辑: 请注意,这类似于C++ convert integer to string at compile time,但不一样。在这里,我明确希望使用 constexpr 而不是宏来帮助泛型编程。

【问题讨论】:

  • 我认为三年时间足以发现如何格式化代码块!
  • 我不完全确定这个“code plz”问题是否完全符合主题,但我认为它很有趣,值得保留。以后可以试一试。
  • C++11/14 和 C++0.786 一样,还是 C++0?嗯,你决定....
  • 其实这对于字面量来说还是很简单的:template&lt;char... cs&gt; auto operator"" _cs() -&gt; std::array&lt;char, sizeof...(cs)+1&gt; { return {cs..., '\0'}; }Live example
  • @dyp,比这更简单:auto operator""_cs(const char* lit) { return lit; } - Live example

标签: c++ c++11


【解决方案1】:

可变参数模板助您一臂之力。 :)

namespace detail
{
    template<unsigned... digits>
    struct to_chars { static const char value[]; };

    template<unsigned... digits>
    constexpr char to_chars<digits...>::value[] = {('0' + digits)..., 0};

    template<unsigned rem, unsigned... digits>
    struct explode : explode<rem / 10, rem % 10, digits...> {};

    template<unsigned... digits>
    struct explode<0, digits...> : to_chars<digits...> {};
}

template<unsigned num>
struct num_to_string : detail::explode<num> {};

与往常一样,这里有一个 live example on Coliru 显示用法和(相关)生成的程序集。


调整这种方法以支持负数也很简单。这是一个更通用的形式,需要用户输入整数的类型:

namespace detail
{
    template<uint8_t... digits> struct positive_to_chars { static const char value[]; };
    template<uint8_t... digits> constexpr char positive_to_chars<digits...>::value[] = {('0' + digits)..., 0};

    template<uint8_t... digits> struct negative_to_chars { static const char value[]; };
    template<uint8_t... digits> constexpr char negative_to_chars<digits...>::value[] = {'-', ('0' + digits)..., 0};

    template<bool neg, uint8_t... digits>
    struct to_chars : positive_to_chars<digits...> {};

    template<uint8_t... digits>
    struct to_chars<true, digits...> : negative_to_chars<digits...> {};

    template<bool neg, uintmax_t rem, uint8_t... digits>
    struct explode : explode<neg, rem / 10, rem % 10, digits...> {};

    template<bool neg, uint8_t... digits>
    struct explode<neg, 0, digits...> : to_chars<neg, digits...> {};

    template<typename T>
    constexpr uintmax_t cabs(T num) { return (num < 0) ? -num : num; }
}

template<typename Integer, Integer num>
struct string_from : detail::explode<(num < 0), detail::cabs(num)> {};

它的用法是这样的:

string_from<signed, -1>::value

live example on Coliru 所示。

【讨论】:

  • 我真的很喜欢这个解决方案,我只想提一点:它不是真的constexpr。我意识到当我想使用它并使用static_assert() 对其进行测试时。不过,将其设为 constexpr 并没有什么大不了的:将 value 数组定义为 constexpr 并在声明中初始化它们(必要)。
  • 为什么第一个template&lt;unsigned... digits&gt;和第二个template&lt;uint8_t... digits&gt;有不同的类型,不应该是一样的吗?
  • 非常好。 positive_to_chars 需要额外的专业化来处理数字正好为零的情况。结果应该是“0”,而不是空字符串。
  • 这仍然是首选方法吗?
  • 感谢您的发现。可变参数模板很漂亮。递归和声明式编程的绝妙应用!
猜你喜欢
  • 2019-10-27
  • 2016-09-06
  • 2022-08-05
  • 2014-11-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多