constexpr
花了我 5 年时间才解决的问题! Jason Turner 演示了几种编译时技术,以便在编译时构造 std::string
,然后将其传递给 std::string_view
在运行时使用。核心问题是,使用 std::string
编译时构造的 new
必须在单个表达式中使用,因为 std::string
必须在同一个 new
中具有相应的 delete
(抱歉近似措辞)。由于代码很长,我把它放在问题的最后。
为了做到这一点,他首先展示了如何通过保留内部
constant/compile-time context
函数来将
std::string
中的数据复制到 std::array
(目前不确定它是否不能仅通过 consteval
函数来实现) ).我根本不明白的是,他如何使用非类型模板参数(NTTP)在大约 19' 处“延长” constexpr
的生命周期,使其超出立即函数调用的范围。
我(可能是错误的)对标准的理解是,NTTP 与具有静态存储持续时间的std::array
相关联:
但我尝试使用一个更简单(且人为的)示例:
template parameter object
直播
所有的尝试都没有成功,正如在提供的链接演示中可以看到的,不同的编译器给了我不同的诊断。
常规模式是template <auto Value>
consteval const auto& make_static() {
return Value;
}
int main() {
[[maybe_unused]] const auto& i = make_static<2>(); // (1)
[[maybe_unused]] static const auto& j = make_static<2>(); // (2)
[[maybe_unused]] static constexpr auto& k = make_static<2>(); // (3)
[[maybe_unused]] constinit static auto& l = make_static<2>(); // (4)
return i;
}
注意
make_static<2>()
甚至抱怨取消引用 gcc 13.2
但主干版本似乎与
nullptr
和
msvc
更一致。
有人可以解释一下我的示例有什么问题,并在此基础上解释 Jason Turner 用例是如何工作的吗?
clang
直播
注意,仅 #include <algorithm>
#include <array>
#include <cstddef>
#include <format>
#include <iostream>
#include <string>
// some algo to generate a fancy std::string, possibly at compile-time
constexpr std::string make_string(std::string_view base,
const std::size_t repeat) {
std::string retval;
for (std::size_t count = 0; count < repeat; ++count) {
retval += base;
}
return retval;
}
// use only at compile-time: must be large enough to hold a copy of the string
// data in "constant evaluated context" NB probably wrong wording above
struct oversized_array {
// static constexpr std::size_t VeryLargeSize = 10 * 1024 * 1024; // Jason
// turner example
static constexpr std::size_t VeryLargeSize =
1000; // Jason Turner value was too large for msvc on godbolt
std::array<char, VeryLargeSize> data{};
std::size_t size; // actually used size, VeryLargeSize max.
};
// copy a string containt into an array "large enough"
// helper for to_right_sized_array below
constexpr auto to_oversized_array(const std::string &str) {
oversized_array result;
std::copy(str.begin(), str.end(), result.data.begin());
result.size = str.size();
return result;
}
// copy a string containt into an array of the same size
// callable is a lambda that is wrapping make_string
// KO FOR CLANG
template <typename Callable>
consteval auto to_right_sized_array(
Callable callable) { // Jason Turner version
// constexpr auto to_right_sized_array(Callable callable) { // seems to be
// enough
constexpr auto oversized = to_oversized_array(callable());
std::array<char, oversized.size> result;
std::copy(oversized.data.begin(),
std::next(oversized.data.begin(), oversized.size),
result.begin());
return result;
}
// merely returns a reference to the NTTP Data
// THE CORE OF MY QUESTION IS HERE
template <auto Data>
consteval const auto &make_static() {
return Data;
}
// wrapping the array-converted string into a string_view
template <typename Callable>
consteval auto to_string_view(Callable callable) {
// std::array as NTTP is C++20?
constexpr auto &static_data = make_static<to_right_sized_array(callable)>();
return std::string_view{static_data.begin(), static_data.size()};
}
int main() {
constexpr auto make_data = []() {
return make_string("Hello compile-time world,", 3);
};
constexpr static auto view = to_string_view(make_data);
std::cout << std::format("{}: {}", view.size(), view);
}
gcc
似乎与
msvc
API 混淆(或者也许我在复制 Jason Turner 代码时犯了一些拼写错误)。
std::string_view
不同意所有不断评估的事情。可能与我之前的一篇文章有关:gcc 和 clang 在持续评估方面表现不同 问题在于,根据非类型模板参数的类型,规则会有所不同。
如果它是类类型,则命名它的 id 表达式将是类型为
clang
如果它是非类类型,则命名它的 id 表达式将是
const T
在这种情况下不适用。
将是(8) 命名类类型
命名一个非类型非引用模板参数,
T 的非类型模板参数 的 id 表达式表示 const T 类型的静态存储持续时间对象,称为模板参数对象,其值为转换为模板参数类型后对应的模板参数。程序中所有此类相同类型、相同值的模板参数都表示同一个模板参数对象。模板参数对象应不断销毁。 [注 3:如果 id-expression那么如果它具有非类类型,那么它就是纯右值。否则,如果它是类类型 T,则它是左值并且具有类型 const T。 — 尾注] 因此
T
Value
中的纯右值 - 尝试绑定对它的引用将实现临时值。所以这实际上是试图返回一个悬空引用。