如何使用模板化的 constexpr 成员函数初始化 constexpr std::array?
How to initialize a constexpr std::array with templated constexpr member functions?
这是对我的问题 的跟进。最后我想创建一个 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;
}
所以,我可以用我的模板 class 创建 constexpr
个“值”。这些值可以在 constexpr std::array
的“初始化程序”列表中使用。但是,如果我想直接在初始化列表中使用我的 class,那么它会编译,但只存储 nullptrs。程序的输出是:
text123
text2
╠╠╠╠╠╠╠╠╠╠╠╠╠╠<½7
╠╠╠╠╠╠╠╠╠╠╠╠╠╠<½7
为什么会这样?或者,有解决办法吗?
使用 Microsoft Visual Studio Community 2019,版本 16.8.2,C++17,调试,X86 编译
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中你可以看到
A constant expression is either
[...]
a prvalue core constant expression whose value satisfies the following constraints: [...] if the value is of pointer type, it holds - address of an object with static storage duration
因此,对于 convArray1
constexpr std::array< const char*, 2> convArray1{ conv123, conv2 };
你必须让 static
conv123
和 conv2
// 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
对象的方法。
您的代码在 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(); }
};
重命名 类 和您喜欢的变量。
这是对我的问题 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;
}
所以,我可以用我的模板 class 创建 constexpr
个“值”。这些值可以在 constexpr std::array
的“初始化程序”列表中使用。但是,如果我想直接在初始化列表中使用我的 class,那么它会编译,但只存储 nullptrs。程序的输出是:
text123
text2
╠╠╠╠╠╠╠╠╠╠╠╠╠╠<½7
╠╠╠╠╠╠╠╠╠╠╠╠╠╠<½7
为什么会这样?或者,有解决办法吗?
使用 Microsoft Visual Studio Community 2019,版本 16.8.2,C++17,调试,X86 编译
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中你可以看到
A constant expression is either [...] a prvalue core constant expression whose value satisfies the following constraints: [...] if the value is of pointer type, it holds - address of an object with static storage duration
因此,对于 convArray1
constexpr std::array< const char*, 2> convArray1{ conv123, conv2 };
你必须让 static
conv123
和 conv2
// 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
对象的方法。
您的代码在 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(); }
};
重命名 类 和您喜欢的变量。