当前位置: 首页 > 知识库问答 >
问题:

为什么std::is invocable\r拒绝返回不可移动类型的函数?

楚羽
2023-03-14

我很好奇std::is_invocable_r的定义,以及它如何与不可移动类型交互。基于我对它应该模仿的语言规则的理解,它在C 20模式下的clang下的libc实现似乎是错误的,所以我想知道我的理解有什么不正确之处。

假设我们有一个不能移动或复制构造的类型,以及一个返回它的函数:

struct CantMove {
  CantMove() = default;
  CantMove(CantMove&&) = delete;
};

static_assert(!std::is_move_constructible_v<CantMove>);
static_assert(!std::is_copy_constructible_v<CantMove>);

CantMove MakeCantMove() { return CantMove(); }

然后可以调用该函数来初始化CantMove对象(我认为是由于复制省略规则):

CantMove cant_move = MakeCantMove();

类型特征同意函数是可调用的,并返回CantMove

using F = decltype(MakeCantMove);
static_assert(std::is_invocable_v<F>);
static_assert(std::is_same_v<CantMove, std::invoke_result_t<F>>);

但是,std::is_invocable_r表示,调用它来生成可转换为CantMove的内容是不可能的,至少在C 20中,在clang下是这样的:

static_assert(!std::is_invocable_r_v<CantMove, F>);

std::is invocable\r的定义是

表达式INVOKE

使用调用

定义调用

INVOKE定义(在本例中)为简单的MakeCantMove()。但关于是否可能进行隐式转换的定义是:

表达式E可以隐式转换为类型T当且仅当声明T=E是格式良好的,对于一些发明的临时变量t([dcl.init])。

但我们在上面看到了CantMove cant_move=MakeCantMove() 。那么,clang接受这个初始化是错误的,还是std::is_invocable_r_v的实现是错误的?还是我对标准的解读有误?

作为记录,我关心这个问题的原因是,像std::move_only_function(我正在使用一个高级端口来连接这个函数的C20)这样的类型,其成员的重载集受到std::is_invocable_r_v的限制,我发现不可能有效地处理返回这种不移动类型的函数。这是故意的吗?如果是,为什么?


共有1个答案

欧阳衡
2023-03-14
匿名用户

那么,clang接受这个初始化是错误的,还是std::is_invocable_r_v的实现是错误的?

这是libc的一个bug。在实现<代码>is_invocable_r时,它使用<代码>is_convertible来确定结果是否可以隐式转换为<代码>T,这是不正确的,因为<代码>is convertible_v

值得注意的是,libstdc和MSVC-STL都有关于这个问题的错误报告,这些报告已经修复。

 类似资料:
  • 问题内容: 我在某处读到,函数应始终仅返回一种类型,因此以下代码被视为错误代码: 我想更好的解决方案是 返回None然后创建一个新的空元组不是更便宜的内存明智的选择吗?或者即使在较大的项目中,这种时差也太小而无法引起注意? 问题答案: 为什么函数应该返回一致类型的值?满足以下两个规则。 规则1-函数具有“类型”-输入映射到输出。它必须返回一致的结果类型,否则它不是函数。一团糟。 从数学上讲,我们说

  • 允许您执行以下操作: 但不是这个: 可能是因为返回类型不是函数签名的一部分。但是是一个类类型,它被赋予一个返回类型,并且知道构造它的函数对象的返回类型。所以这里可能有编译器错误。 为什么没有编译器错误?

  • 我有以下示例代码,简化为基本代码,它使用GCC6.1、GCC7.0head和Visual Studio 2015/2017 RC编译,但不使用任何clang版本。 clang告诉我:prog.cc:12:34:error:'_p'是'outer::foo'返回std::make_tuple(c_p...)的私有成员;

  • 我知道了从< code>std::async返回的< code>future具有某种特殊共享状态的原因,通过这种状态,< code >等待返回的future发生在future的析构函数中。但是当我们使用< code>std::pakaged_task时,它的未来不会表现出同样的行为。要完成打包的任务,必须从< code>packaged_task显式调用< code>future对象上的< cod

  • 问题内容: 我有一个模块分开的应用程序。有几个实体和CSV模块。CSV模块仅支持struct(Entity),但我想使CSV模块可与任何类型的实体一起使用。现在,它的工作方式如下:Csv模块从通道接收数据并将其严格转换为struct。我如何实现动态返回类型,因此它可以与任何类型的Entity一起使用,而不仅限于 问题答案: 快速/肮脏的解决方案: 返回接口{},但是您最终欺骗了编译器,而类型检查的