Oer*_*ted 7 c++ compile-time c++20 non-type-template-parameter
在C++ Weekly - Ep 313 -constexpr我花了 5 年时间才解决的问题!Jason Turner 演示了几种编译时技术,以便std::string在编译时构造 a,然后将其传递给 astd::string_view以在运行时使用。
核心问题是,std::string使用new编译时构造的std::string必须在单个表达式中使用,因为在相同的表达式中new必须有相应的(抱歉近似的措辞)。deleteconstant/compile-time context
由于代码很长,我把它放在问题的最后。
为了做到这一点,他首先展示了如何通过将数据保存在函数内部来std::string将数据从 复制到 an (我目前不确定它是否只能通过函数来实现)。std::arrayconstevalconstexpr
我完全不明白的是,他如何使用非类型模板参数std::array(NTTP)将立即函数调用的生命周期“延长”到大约 19 分钟。
我对标准的理解(可能是错误的)是 NTTPtemplate parameter object与静态存储持续时间相关联:
命名类类型 T 的非类型模板参数的 id 表达式表示类型为 const T 的静态存储持续时间对象,称为模板参数对象,它与模板参数等价([temp.type])对应的模板参数已转换为模板参数的类型([temp.arg.nontype])。没有两个模板参数对象是模板参数等效的。
然而我尝试使用一个更简单(且人为的)示例:
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;
}
Run Code Online (Sandbox Code Playgroud)
正如在提供的链接演示中可以看到的那样,许多尝试都没有成功。不同的编译器给了我不同的诊断。
常规模式make_static<2>()不被认为是在常量表达式中,并且它返回对临时值的引用(据我所知,这超出了 Jason Turner 的目标)。
注意:GCC 13.2 (2023-07-27) 甚至抱怨取消引用 a nullptr,但主干版本似乎与MSVC和Clang更一致。
我的示例有什么问题,在这种情况下,Jason Turner 用例是如何工作的?
#include <algorithm>
#include <array>
#include <cstddef>
#include <format>
#include <iostream>
#include <string>
// Some algorithm 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: it must be large enough to hold
// a copy of the string data in "constant evaluated context"
// NB: It is 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 Compiler Explorer
std::array<char, VeryLargeSize> data{};
std::size_t size; // Actually the used size, VeryLargeSize max.
};
// Copy a string containt into an array "large enough".
// It is a 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.
// The 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) { // It 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);
}
Run Code Online (Sandbox Code Playgroud)
注意:只有 GCC 接受此版本。MSVC 似乎与std::string_viewAPI 混淆了(或者可能是我在复制 Jason Turner 的代码时犯了一些拼写错误)。Clang 不同意所有不断评估的事情。这可能与我之前的一篇文章有关:GCC 和 Clang 在持续评估方面表现不同
问题在于,规则根据非类型模板参数的类型而有所不同。
\nconst T。T。请注意,您引用的部分仅适用于类类型非类型模板参数。
\n但是您传递的是 int (这是一种非类类型),因此第(8)段在这种情况下不适用。
\n\n13.2 模板参数 [temp.param]
\n
\n (8)命名类类型T的非类型模板参数的 id 表达式表示 const T类型的静态存储持续时间对象,称为模板参数对象,其值为转换为模板参数的类型后相应模板参数的类型。程序中所有此类相同类型、相同值的模板参数都表示同一个模板参数对象。模板参数对象应不断销毁。[注3:如果id 表达式命名一个非类型非引用模板参数,那么如果它具有非类 type ,那么它就是纯右值。否则,如果它是类类型T,则它是左值并且类型为const T。\xe2\x80\x94 尾注]
\n
因此,Value将是make_static- 中的纯右值,并且尝试绑定对它的引用将实现临时值。
\n因此,这实际上尝试返回悬空引用。
解决方法是将非类类型非类型模板参数包装到类类型非类型模板参数中,并返回对此的引用,例如:godbolt
\n#include <type_traits>\n\ntemplate<class T>\nstruct value_holder { T value; };\n\n\ntemplate<auto V>\nconsteval auto const& make_static() {\n if constexpr(std::is_class_v<decltype(V)>) {\n return V;\n } else {\n // V is of non-class type, and therefore a prvalue\n // => wrap it in a class type so that we can return a reference\n return make_static<value_holder{V}>().value;\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
127 次 |
| 最近记录: |