std::transform来自<algorithm>标题的内容适用于范围,它“使”我们能够使用范围作为它们本身的函子(在范畴论的意义上(\xc2\xb9))。std::transform是基于迭代器的,是的,但std::ranges::views::transform不是,并且它的签名与函数语言中相应函数的签名密切匹配(对两个参数的不同顺序取模,但这没什么大不了的)。
当我看到这个问题(以及在回答它的过程中)时,我了解到 C++23 引入了std::optional<T>::transform,它std::optional也创建了一个函子。
所有这些消息确实让我兴奋,但我不禁想到函子是通用的,如果有一个统一的接口就好了transform任何函子都有一个统一的接口会很好,例如 Haskell 中的情况。
这让我认为类似的对象std::ranges::views::transform(具有不同的名称,而不是暗示ranges)可以成为一个定制点,STL不仅可以为范围定制,还可以为std::optionalSTL中的任何其他函子定制,而程序员可以为其用户定义的类定制它。
非常相似,C++23 也引入了std::optional<T>::and_then,它基本上是 的一元绑定std::optional。我不知道有任何类似的函数可以实现范围的单子绑定,但 C++20本质上是withsome_range | std::ranges::views::transform(f) | std::ranges::views::join的单子绑定。some_rangef
这让我认为可能存在一些通用接口,命名它mbind,人们可以选择使用任何类型。例如,STL 将选择std::optional通过按照 来实现它。std::optional<T>::and_then
该语言是否有机会或有计划有一天支持这种通用性?
\n我当然可以看到一些问题。今天 不完全是,破坏依赖于基于 SFINAE 的无效代码检测的代码是可以的。std::ranges::views::transform(some_optional, some_func)无效,因此某些代码可能依赖于 SFINAE。让它突然工作会破坏代码。
(\xc2\xb9) 关于 …
class stacktrace_entry {
public:
string description() const;
string source_file() const;
uint_least32_t source_line() const;
/* ... */
};
Run Code Online (Sandbox Code Playgroud)
struct source_location {
// source location field access
constexpr uint_least32_t line() const noexcept;
constexpr uint_least32_t column() const noexcept;
constexpr const char* file_name() const noexcept;
constexpr const char* function_name() const noexcept;
/* ... */
};
Run Code Online (Sandbox Code Playgroud)
它们的目的基本相同,为什么它们有差异,特别是 中没有列stacktrace_entry,或者甚至不共享同一类?
我正在使用 C++23std::optional添加,但我不知道如何优雅地访问对象的值(如果optional处于活动状态)。
我知道我可以使用if,但这就是C ++20 。
我真的很喜欢 C++23 API 的变化,但我不知道如何跳过实现身份的样板。例如:
#include <functional>
#include <iostream>
#include <optional>
void print(std::optional<std::string>& name) {
name.transform([](std::string& x) {
std::cout << x << std::endl;
// I just want to print, without modifying optional, but next line is required
return x;
});
}
int main() {
std::optional<std::string> name{{"Bjarne"}};
print(name);
}
Run Code Online (Sandbox Code Playgroud)
感觉就像std::optional缺少invoke会员功能一样。
注意:在我的示例中,我在转换后不会链接任何其他内容,但为了简洁起见,我关心可选内容是否被修改。
关键字constexpr在引入 C++11 标准时对其函数实施了相当严格的限制。C++14 和 C++20 放宽了这些限制(最值得注意):
return语句static_assert等。try并且asmC++23 进一步软化了这些限制。从我在cppreference中看到的,constexprfor函数似乎只剩下以下含义:
C++23 甚至删除了 constexpr 函数必须在编译时对于p2448r2中的任何类型“可计算”的限制。根据我的理解,这完全消除了constexpr在编译时评估函数的想法。
是这样吗?如果是这样,constexpr函数还有什么用处呢?
在下面的示例中,我们有两个不同类型的对象,但恰好在内存中彼此相邻。
struct X {
alignas(long) int val = 1;
};
struct Y {
long val = 2;
};
[...]
std::byte* buf = new std::byte[sizeof(X) + sizeof(Y)];
X* x = new (buf) X();
new (buf + sizeof(X)) Y();
Run Code Online (Sandbox Code Playgroud)
给定x,以及我们对 的实例Y实际位于内存中相对于 的位置的了解,是否有任何方法可以从符合 C++20 或 C++23 的规范x派生指向 的实例的指针?Yx
换句话说,以下代码是否符合标准,或者是否有任何方法使其符合标准:
Y* y = std::launder(reinterpret_cast<Y*>(reinterpret_cast<std::byte*>(x) + sizeof(X)));
y->val = 3;
Run Code Online (Sandbox Code Playgroud)
我怀疑该代码不符合 C++20 或 C++23 标准,原因如下:
(1) P1839R5表明实际上不可能首先获得指向对象表示形式的可用指针。
(2) 即使考虑到 P1839R5 中修订的指针算术规则,您似乎仍然无法从指向一个对象表示形式的指针转到另一种对象表示形式。
来自http://eel.is/c++draft/class.member.lookup#1:
甲搜索在范围
X为名称N从程序点P是在单个搜索X用于N从P除非X是一个类或类模板的范围T,在这种情况下,下面的步骤定义搜索的结果。[注 1:结果只有在
N是转换函数 id或单次搜索找不到任何东西时才会有所不同。—尾注]
我很难理解 Note。似乎从类范围中进行“单一搜索”会在名称空间范围内找到前面的声明,因为名称空间范围包含类范围。但是,正如我们所知,如果名称也被声明为非依赖基类的成员,则基类成员优先于命名空间成员。Note 1似乎与此相矛盾,因为它基本上是在说“如果N不是转换函数 ID,那么您可以只进行普通的单一搜索,并且只有在找不到任何内容时,才使用本节中的过程”。但是单次搜索将通过找到命名空间范围声明而成功,而类成员查找将产生不同的结果。
我的理解错误在哪里?
自从std::generator正在进入 CPP23,我正在尝试 MSVC 的不完整版本。
然而,我注意到,yield与 一起使用时,它似乎恰好丢失了一个std::views::take。这是示例:
#include <iostream>
#include <ranges>
#include <experimental/generator>
std::experimental::generator<int> GeneratorFn(void) noexcept
{
co_yield 1;
co_yield 2;
co_yield 3;
co_yield 4;
co_yield 5;
co_yield 6;
co_yield 7;
co_yield 8;
co_yield 9;
co_return;
}
int main(int argc, char** args) noexcept
{
auto Ret = GeneratorFn();
for (auto&& i : Ret | std::views::take(2))
std::cout << i << '\n';
for (auto&& i : Ret | std::views::take(3))
std::cout << i << '\n';
for (auto&& i …Run Code Online (Sandbox Code Playgroud) 有一个相当典型的任务是同时对两个数组进行排序,假设数组的相同索引元素形成虚拟对,并对其进行排序。这样的问题至少在 10 年前就出现过:boost zip_iterator 和 std::sort
现在这个任务可以使用range-v3库来解决:
#include <array>
#include <range/v3/all.hpp>
int main() {
auto x = std::array{ 3, 2, 4, 1 };
auto y = std::array{'A', 'B', 'C', 'D'};
ranges::sort( ranges::views::zip( x, y ) );
// here x = {1,2,3,4}, y={'D','B','A','C'}
}
Run Code Online (Sandbox Code Playgroud)
在线演示: https: //gcc.godbolt.org/z/WGo4vGsx5
在 C++23 中出现了std::ranges::zip_view,我的期望是可以仅使用标准库编写相同的程序:
#include <array>
#include <ranges>
#include <algorithm>
int main() {
auto x = std::array{ 3, 2, 4, 1 };
auto y = std::array{'A', 'B', 'C', 'D'};
std::ranges::sort( std::views::zip( …Run Code Online (Sandbox Code Playgroud) 我目前正在使用 C++ 模块,尝试对我们公司的代码 C++ 基础进行现代化改造,以使用模块来实现核心功能。特别是即将推出的 C++23std模块,作为预编译头的更好替代品,它看起来非常有趣。
所以我使用的是Visual Studio 2022 17.5 Preview 2.0,它对模块有初步支持std。顺便说一句,我遇到了编译器内部错误,我向Microsoft报告了这个错误。
在我们所有的 C++ 源文件中,现在都有一个import std;语句,而且效果很好。每个应该位于std命名空间中的标识符似乎都按预期导出。我测量到与之前使用预编译头相比,编译时间略有减少。
我发现,如果你,你在任何标准import std;C++ 头文件中都会遇到很多奇怪的编译错误,因为 Microsoft 编译器会感到困惑并抱怨重新定义。所以我小心翼翼地将它们全部删除。 #include <>
我的问题是,标准库中定义了一些宏(主要是在 C 兼容库中),这些宏显然没有导出,因为 C++ 模块在设计上从不导出宏。
我们的代码库使用的标准宏数量非常有限,但我认为很难避免它们。以下是它们的简短列表(不确定是否完整):
stdouterrnova_start, va_arg,va_end对于va_*宏,我#include <stdarg.h>和它在 VS 2022 上编译得很好,尽管它打破了我之前提到的规则。这可能是因为该标头几乎只有宏。但对于stdout和errno,我不知道该怎么办。
C++23 是否指定在导入模块时如何访问重要的标准宏,例如stdout或?有没有好的解决方法?errnostd
我正在尝试Kokkos 参考 mdspan 实现,看看它是否可以用来简化我们代码库中的一些代码。我直观地认为可能的一件事是选择二维的一行std::mdspan并将其分配给 a std::span,例如有点像
float* data = ...
std::mdspan matrix (data, 2, 5);
std::span vector = matrix[0]; // <-- should be a std::span<float, std::dynamic_extent> viewing row 0 of matrix
Run Code Online (Sandbox Code Playgroud)
经过一番研究,我没有找到一种明显直接的方法来实现这一目标,无论是使用 mdspan 的成员函数还是使用 std 库中的免费函数。目前我看到的唯一可能性是回到原始指针级别并编写自己的自由函数来执行此操作,这并不像我预期的那么优雅。我是否忽略了某些东西,或者这真的不是 mdspan 的功能吗?
c++ ×10
c++23 ×10
std-ranges ×2
c++-modules ×1
c++20 ×1
constexpr ×1
functor ×1
generator ×1
mdspan ×1
monads ×1
pointers ×1
stack-trace ×1
stdoptional ×1