非类型模板参数的静态存储持续时间和生命周期是多少以及如何将其用于编译时计算

问题描述 投票:0回答:1

C++ Weekly - Ep 313 -

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 相关联:

命名类类型 T 的非类型模板参数的 id 表达式表示 const T 类型的静态存储持续时间对象,称为模板参数对象,其模板参数等价于 ([temp.type])转换为模板参数类型后的相应模板参数 ([temp.arg.nontype])。没有两个模板参数对象是模板参数等效的。

但我尝试使用一个更简单(且人为的)示例:

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

不被视为在常量表达式中,并且它返回对临时值的引用(这击败了 AFAIU、Jason Turner 的目标)。

注意

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 在持续评估方面表现不同

    

问题在于,根据非类型模板参数的类型,规则会有所不同。
c++ c++20 compile-time non-type-template-parameter
1个回答
0
投票

如果它是类类型,则命名它的 id 表达式将是类型为

clang
    的左值。
  • 如果它是非类类型,则命名它的 id 表达式将是 
    const T
  • 类型的右值。
  • 请注意,您引用的部分仅适用于类类型非类型模板参数。
  • 但是您传递的是 int (这是非类类型),因此段落
(8)

在这种情况下不适用。

13.2 模板参数[temp.param]

(8) 命名类类型
T 的非类型模板参数 的 id 表达式表示 const T 类型的静态存储持续时间对象,称为模板参数对象,其值为转换为模板参数类型后对应的模板参数。程序中所有此类相同类型、相同值的模板参数都表示同一个模板参数对象。模板参数对象应不断销毁。 [注 3:如果 id-expression

命名一个非类型非引用模板参数,

那么如果它具有非类类型,那么它就是纯右值。否则,如果它是类类型 T,则它是左值并且具有类型 const T。 — 尾注] 因此

T
将是

Value

 中的纯右值 - 尝试绑定对它的引用将实现临时值。
所以这实际上是试图返回一个悬空引用。


解决方法是将非类类型非类型模板参数包装到类类型非类型模板参数中,并返回对此的引用,例如:

godbolt

make_static


© www.soinside.com 2019 - 2024. All rights reserved.