标签: c++23

std::some-namespace::transform 有一天可以支持任何函子吗?

std::transform来自<algorithm>标题的内容适用于范围,它“使”我们能够使用范围作为它们本身的函子(在范畴论的意义上(\xc2\xb9))。std::transform是基于迭代器的,是的,但std::ranges::views::transform不是,并且它的签名与函数语言中相应函数的签名密切匹配(对两个参数的不同顺序取模,但这没什么大不了的)。

\n

当我看到这个问题(以及在回答它的过程中)时,我了解到 C++23 引入了std::optional<T>::transform,它std::optional也创建了一个函子。

\n

所有这些消息确实让我兴奋,但我不禁想到函子是通用的,如果有一个统一的接口就好了transform任何函子都有一个统一的接口会很好,例如 Haskell 中的情况。

\n

这让我认为类似的对象std::ranges::views::transform(具有不同的名称,而不是暗示ranges)可以成为一个定制点,STL不仅可以为范围定制,还可以为std::optionalSTL中的任何其他函子定制,而程序员可以为其用户定义的类定制它。

\n

非常相似,C++23 也引入了std::optional<T>::and_then,它基本上是 的一元绑定std::optional。我不知道有任何类似的函数可以实现范围的单子绑定,但 C++20本质上是withsome_range | std::ranges::views::transform(f) | std::ranges::views::join的单子绑定。some_rangef

\n

这让我认为可能存在一些通用接口,命名它mbind,人们可以选择使用任何类型。例如,STL 将选择std::optional通过按照 来实现它。std::optional<T>::and_then

\n

该语言是否有机会或有计划有一天支持这种通用性?

\n
\n

我当然可以看到一些问题。今天std::ranges::views::transform(some_optional, some_func)无效,因此某些代码可能依赖于 SFINAE。让它突然工作会破坏代码。 不完全是,破坏依赖于基于 SFINAE 的无效代码检测的代码是可以的。

\n
\n

(\xc2\xb9) 关于 …

c++ monads functional-programming functor c++23

8
推荐指数
1
解决办法
421
查看次数

为什么 C++23 stacktrace_entry 与 source_location 不同?

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++ stack-trace c++23

8
推荐指数
1
解决办法
534
查看次数

如何在不编写标识样板的情况下访问 C++23 std::Optional 中包含的值?

我正在使用 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会员功能一样。

注意:在我的示例中,我在转换后不会链接任何其他内容,但为了简洁起见,我关心可选内容是否被修改。

c++ stdoptional c++23

8
推荐指数
1
解决办法
1502
查看次数

鉴于 C++23 对 constexpr 的放宽,constexpr 不能成为默认值吗?

关键字constexpr在引入 C++11 标准时对其函数实施了相当严格的限制。C++14 和 C++20 放宽了这些限制(最值得注意):

  • C++14 允许多个return语句static_assert等。
  • C++20 允许try并且asm

C++23 进一步软化了这些限制。从我在cppreference中看到的,constexprfor函数似乎只剩下以下含义:

  • 它不能是协程
  • 对于构造函数和析构函数,该类必须没有虚拟基类
  • 对于 constexpr 函数模板和类模板的 constexpr 成员函数,至少一种特化必须满足上述要求。

C++23 甚至删除了 constexpr 函数必须在编译时对于p2448r2中的任何类型“可计算”的限制。根据我的理解,这完全消除了constexpr在编译时评估函数的想法。

是这样吗?如果是这样,constexpr函数还有什么用处呢?

c++ language-design constexpr constexpr-function c++23

8
推荐指数
1
解决办法
1100
查看次数

给定两个不同类型的对象及其在内存中的相对位置,我可以从指向另一个对象的指针派生出指向另一个对象的指针吗?

在下面的示例中,我们有两个不同类型的对象,但恰好在内存中彼此相邻。

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 中修订的指针算术规则,您似乎仍然无法从指向一个对象表示形式的指针转到另一种对象表示形式。

c++ pointers language-lawyer c++20 c++23

8
推荐指数
1
解决办法
268
查看次数

C++类成员名称查找规则中注1是什么意思?

来自http://eel.is/c++draft/class.member.lookup#1

搜索在范围X为名称N从程序点P是在单个搜索X用于NP除非X是一个类或类模板的范围T,在这种情况下,下面的步骤定义搜索的结果。

[注 1:结果只有在N转换函数 id或单次搜索找不到任何东西时才会有所不同。—尾注]

我很难理解 Note。似乎从类范围中进行“单一搜索”会在名称空间范围内找到前面的声明,因为名称空间范围包含类范围。但是,正如我们所知,如果名称也被声明为非依赖基类的成员,则基类成员优先于命名空间成员。Note 1似乎与此相矛盾,因为它基本上是在说“如果N不是转换函数 ID,那么您可以只进行普通的单一搜索,并且只有在找不到任何内容时,才使用本节中的过程”。但是单次搜索将通过找到命名空间范围声明而成功,而类成员查找将产生不同的结果。

我的理解错误在哪里?

c++ language-lawyer c++23

7
推荐指数
1
解决办法
158
查看次数

关于 MSVC std::experimental::generator 中的暂停-恢复数据丢失

自从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)

c++ generator c++-coroutine std-ranges c++23

7
推荐指数
1
解决办法
229
查看次数

使用 C++23 zip 视图对两个数组进行排序

有一个相当典型的任务是同时对两个数组进行排序,假设数组的相同索引元素形成虚拟对,并对其进行排序。这样的问题至少在 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++ std-ranges c++23

7
推荐指数
1
解决办法
672
查看次数

如何在 C++23 中将标准库宏与 std 模块一起使用

我目前正在使用 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++ 模块在设计上从不导出宏

我们的代码库使用的标准宏数量非常有限,但我认为很难避免它们。以下是它们的简短列表(不确定是否完整):

  • stdout
  • errno
  • va_start, va_arg,va_end

对于va_*宏,我#include <stdarg.h>和它在 VS 2022 上编译得很好,尽管它打破了我之前提到的规则。这可能是因为该标头几乎只有宏。但对于stdouterrno,我不知道该怎么办。

C++23 是否指定在导入模块时如何访问重要的标准宏,例如stdout或?有没有好的解决方法?errnostd

c++ c++-modules c++23

7
推荐指数
1
解决办法
832
查看次数

将 std::mdspan 的一行作为 std::span 访问

我正在尝试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++ c++23 mdspan

7
推荐指数
1
解决办法
278
查看次数