jac*_*bsa 13 c++ sfinae type-traits language-lawyer c++20
我很好奇的定义std::is_invocable_r以及它如何与不可移动类型交互。根据我对它应该模拟的语言规则的理解,它在 C++20 模式下 clang 下的 libc++ 实现似乎是错误的,所以我想知道我的理解有什么不正确。
假设我们有一个不能移动或复制构造的类型,以及一个返回它的函数:
\nstruct CantMove {\n CantMove() = default;\n CantMove(CantMove&&) = delete;\n};\n\nstatic_assert(!std::is_move_constructible_v<CantMove>);\nstatic_assert(!std::is_copy_constructible_v<CantMove>);\n\nCantMove MakeCantMove() { return CantMove(); }\nRun Code Online (Sandbox Code Playgroud)\n然后可以调用该函数来初始化对象CantMove(我相信由于复制省略规则):
CantMove cant_move = MakeCantMove();\nRun Code Online (Sandbox Code Playgroud)\n并且类型特征同意该函数是可调用的并返回CantMove:
using F = decltype(MakeCantMove);\nstatic_assert(std::is_invocable_v<F>);\nstatic_assert(std::is_same_v<CantMove, std::invoke_result_t<F>>);\nRun Code Online (Sandbox Code Playgroud)\n但std::is_invocable_r表示不可能调用它来产生可转换为 的东西CantMove,至少在 C++20 下 clang 中是这样:
static_assert(!std::is_invocable_r_v<CantMove, F>);\nRun Code Online (Sandbox Code Playgroud)\n的定义是std::is_invocable_r
\n\n当被视为未计算的操作数时,该表达式
\nINVOKE<R>(declval<Fn>(), declval<ArgTypes>()...)是格式良好的
被INVOKE<R>定义为
\n\n定义
\nINVOKE<R>(f, t1, t2, \xe2\x80\xa6, tN)为 [...]INVOKE(f, t1, t2, \xe2\x80\xa6, tN)隐式转换为R.
并(在本例中)简单地INVOKE 定义MakeCantMove()为. 但是隐式转换是否可能的定义是这样的:
\n\n对于某些发明的临时变量([dcl.init]) ,当且仅当声明格式良好时,表达式
\nE可以隐式转换为类型。TT t=E;t
但我们在上面看到CantMove cant_move = MakeCantMove();编译器接受了。那么 clang 接受这个初始化是错误的,还是执行std::is_invocable_r_v错误的?或者我对标准的解读是错误的?
根据记录,我关心这个问题的原因是像这样的类型std::move_only_function(我正在使用 C++20 的高级端口)其成员的重载集受限制std::is_invocable_r_v,并且我发现它\ 不可能有效地使用返回这样的无移动类型的函数。这是设计使然吗?如果是这样,为什么?
康桓瑋*_*康桓瑋 13
那么 clang 接受这个初始化是错误的,还是执行
std::is_invocable_r_v错误的?
这是libc++的一个bug。在的实现is_invocable_r中,它用于is_convertible确定结果是否可以隐式转换为T,这是不正确的,因为is_convertible_v<T, T>是false对于不可移动类型,在这种情况下std::declval会添加对 的右值引用T。
值得注意的是,libstdc++和MSVC-STL都有关于此问题的错误报告,并且已修复。
| 归档时间: |
|
| 查看次数: |
775 次 |
| 最近记录: |