如何使用模板化的constexpr成员函数初始化constexpr std::数组



这是我在这里提出的问题的后续内容。最后,我想创建一个constexpr std::array,其中包含带有附加运行索引的文本。

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

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

请参阅以下代码,其中标记了函数main中的非工作行。

#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";值";使用我的模板化类。这些值可以用在";初始化器";CCD_ 3的列表。但是,如果我想直接在初始值设定项列表中使用我的类,那么它会编译,但只存储nullptr。程序的输出为:

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

为什么会发生这种情况?或者,有解决方案吗


使用Microsoft Visual Studio Community 2019编译,版本16.8.2,C++17,调试,X86

您的代码在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; }
};

并去掉其它转换算子和CCD_ 4方法。

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(); }
};

根据需要重命名类和变量。

constexpr std::array< const char*, 2> convArray2{ Converter<2, BaseString>(), 
Converter<2, BaseString>().data() };

在这里,您存储的是指向临时变量的指针——两个Converter对象都在;之后存在。正在取消对指针UB的引用。

Clang拒绝接受这样的代码,给出了非常有用的信息:

<source>:51:43: note: pointer to subobject of temporary is not a constant expression
<source>:51:55: note: temporary created here
constexpr std::array< const char*, 2> convArray2{ Converter<2, BaseString>(), Converter<2, BaseString>().data() };
^
2 errors generated.
Execution build compiler returned: 1

我不确定具体的constexpr规则,但即使要编译,代码也是不安全的。

在Cpp Reference中,您可以看到

常量表达式是[…]一个prvalue核心常量表达式,其值满足以下约束:[…]如果该值为指针类型,则它包含具有静态存储持续时间的对象的地址

因此,对于convArray1

constexpr std::array< const char*, 2> convArray1{ conv123, conv2 };

你必须制作staticconv123conv2

// VVVVVV
static constexpr Converter<123, BaseString> conv123{};
static constexpr auto conv2 = Converter<2, BaseString>();
// ^^^^^^

因为在没有静态存储的情况下,不能从指针中获得常量表达式。

对于convArray2

constexpr std::array< const char*, 2> convArray2{ Converter<2, BaseString>(), Converter<2, BaseString>().data() };

我看不到从临时对象内的指针获取constexpr对象的方法。

最新更新