首页 > 解决方案 > 如何使用模板化的 constexpr 成员函数初始化 constexpr std::array?

问题描述

这是我在此处给出的问题的后续。最后,我想创建一个constexpr std::array带有附加运行索引的包含文本。

我想尝试与上一个问题不同的方法。

几乎所有内容,我在下面的代码中所做的是 constexpr。但也许,这只是返回指向不再存在的变量的指针的老问题。但是,我对此表示怀疑。

请参阅以下代码,其中标记了函数 main 中的 not working 行。

#include <iostream>
#include <algorithm>
#include <iterator>
#include <array>
#include <string>

// Some example text
static constexpr const char BaseString[]{ "text" };

// To create something like "text123" as constexpr
template <const size_t numberToConvert, const char* Text>
class Converter {
public:
    // Some helper variables
    static constexpr size_t TextLength{ std::char_traits<char>::length(Text) };
    static constexpr size_t NumberOfDigits{ ([]() constexpr noexcept {size_t result = 0; int temp = numberToConvert; for (; temp != 0; temp /= 10) ++result; return result; }()) };
    static constexpr size_t ArrayLength{ (numberToConvert ? 1u : 2u) + NumberOfDigits + TextLength };

    // Here we will build the text
    char buf[ArrayLength]{};

    // Constructor: Convert number to character digits
    constexpr Converter() noexcept {
        size_t i{ 0 };  for (; i < TextLength; ++i) buf[i] = Text[i]; // Copy text
        if (numberToConvert == 0) buf[i] = '0';     
        else {
            i = NumberOfDigits + TextLength - 1;    // Convert number to character digits
            int number = numberToConvert; for (; number; number /= 10)
                buf[i--] = number % 10 + '0';
        }
    }
    // cast operator
    constexpr operator const char* () const noexcept { return buf; }
    // For test purposes
    constexpr const char* data() const noexcept { return buf; }
};

// Driver program
int main() {

    // Temporaray constexprs
    constexpr Converter<123, BaseString> conv123{};     // Default construction
    constexpr auto conv2 = Converter<2, BaseString>();  // Assign / copy

    // Build constexpr std::array and initialize it with constexprs
    constexpr std::array< const char*, 2> convArray1{ conv123, conv2 };
    // Show that it works
    std::copy(convArray1.begin(), convArray1.end(), std::ostream_iterator<const char*>(std::cout, "\n"));

    // Does compile, but not work. Array will be initialized with nullptr *******************************************
    constexpr std::array< const char*, 2> convArray2{ Converter<2, BaseString>(), Converter<2, BaseString>().data() };
    std::cout << convArray2[0] << '\n' << convArray2[0] << '\n';

    return 0;
}

所以,我可以constexpr用我的模板类创建“值”。这些值可用于constexpr std::array. 但是,如果我想直接在初始化列表中使用我的类,那么它会编译,但只存储 nullptrs。程序的输出是:

text123
text2
╠╠╠╠╠╠╠╠╠╠╠╠╠╠<½7
╠╠╠╠╠╠╠╠╠╠╠╠╠╠<½7

为什么会这样?或者,有解决办法吗?


使用 Microsoft Visual Studio Community 2019 版本 16.8.2、C++17、Debug、X86 编译

标签: c++arraystemplatesc++17constexpr

解决方案


您的代码在 MSVC 上生成编译时悬空指针(这应该是不可能的)。

修理:

template <const size_t numberToConvert, const char* Text>
class Converter {
  // blah
  std::array<char, ArrayLength> buf{};
  constexpr operator std::array<char, ArrayLength>() const { return buf; }
  constexpr std::array<char, ArrayLength> get() const { return *this; }
};

并删除其他转换运算符和data方法。

template<const size_t numberToConvert, const char* Text>
constexpr auto Converted = Converter<numberToConvert, Text>{}.get();

现在Converted<blah...>.data()用来获取你想要的指针。

如果你真的想要隐式转换为字符指针:

template<const size_t numberToConvert, const char* Text>
struct Convertest {
  constexpr operator char const*() const { return Converted<numberToConvert,Text>.data(); }
};

随意重命名类和变量。


推荐阅读