E_3*_*E_3 12 c++ c++-concepts c++20
我在开始了解 C++20 概念时遇到一些困难。我想定义一个概念,要求类有一个名为的成员,count_该成员必须是类型int:
#include <concepts>\n\ntemplate <typename T>\nconcept HasCount = requires(T thing) {\n { thing.count_ } -> std::same_as<int>;\n};\nRun Code Online (Sandbox Code Playgroud)\n以下结构应该满足这个概念:
\nstruct BaseTableChunk {\n BaseTableChunk* next_;\n int count_ = 0;\n int data_[1000];\n};\nRun Code Online (Sandbox Code Playgroud)\n然后,以下代码无法编译:
\ntemplate <HasCount Chunk>\nclass BaseTable {\n void doSomething();\n};\n\nint main() {\n BaseTable<BaseTableChunk> table{};\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n编译器给出以下错误:
\nnote: constraints not satisfied\nIn file included from /usr/include/c++/10/compare:39,\n from /usr/include/c++/10/bits/stl_pair.h:65,\n from /usr/include/c++/10/bits/stl_algobase.h:64,\n from /usr/include/c++/10/bits/char_traits.h:39,\n from /usr/include/c++/10/ios:40,\n from /usr/include/c++/10/ostream:38,\n from /usr/include/c++/10/iostream:39,\n from Minimal2.cxx:1:\n/usr/include/c++/10/concepts:57:15: required for the satisfaction of \xe2\x80\x98__same_as<_Tp, _Up>\xe2\x80\x99 [with _Tp = int&; _Up = int]\n/usr/include/c++/10/concepts:62:13: required for the satisfaction of \xe2\x80\x98same_as<int&, int>\xe2\x80\x99\n/usr/include/c++/10/concepts:57:32: note: the expression \xe2\x80\x98is_same_v<_Tp, _Up> [with _Tp = int&; _Up = int]\xe2\x80\x99 evaluated to \xe2\x80\x98false\xe2\x80\x99\n 57 | concept __same_as = std::is_same_v<_Tp, _Up>;\nRun Code Online (Sandbox Code Playgroud)\n据我了解,thing.count_评估返回 anint&而不是 an int,这不是我所期望的。
我应该测试吗{ thing.count_ } -> std::same_as<int&>?(然后编译。)这对我来说似乎相当违反直觉。
use*_*522 19
如果是声明类型count_的成员,则表达式也是类型,并且表达式的值类别是左值。thingintthing.count_int
表格的复合要求{ E } -> C将测试是否decltype((E))满足C。换句话说,它测试表达式的类型( E而不是可能命名的实体的类型)是否E满足概念。
通过获得的表达式的类型decltype((E))将值类别转换为类型的引用限定。右值导致非引用,左值导致左值引用,x值导致右值引用。
因此,在您的示例中,类型将是int&,但概念std::same_as需要类型的严格匹配,包括其引用限定,从而使其失败。
一个简单的解决方案是仅测试int&:
{ thing.count_ } -> std::same_as<int&>;
Run Code Online (Sandbox Code Playgroud)
问题评论中也提到了类似的解决方案,自 C++23 起:
{ auto(thing.count_) } -> std::same_as<int>
Run Code Online (Sandbox Code Playgroud)
auto被推导为int,并且函数式强制转换表达式int(...)始终是纯右值,因此它永远不会产生引用限定类型。
另一种选择是编写一个概念来替换std::same_as,它不检查确切的类型相等性,而是首先将std::remove_reference_torstd::remove_cvref_t应用于类型,具体取决于您想要如何处理const-mismatch。值得注意的是,第一个解决方案不会接受const int成员或const- 具有T成员资格int,而第二个解决方案会(因为auto永远不会推导出 a const)。
但是,如果您打算检查 是否具有完全thing类型的成员,那么您应该小心。如果它具有引用类型的成员,则上述所有解决方案也将得到满足。int thingint
使用复合需求无法轻松排除参考案例,但可以使用嵌套需求来代替:
template <typename T>
concept HasCount = requires(T thing) {
requires std::same_as<decltype(thing.count_), int>;
};
Run Code Online (Sandbox Code Playgroud)
嵌套要求(由另一个引入requires)不仅检查表达式的有效性,还检查它的计算结果是否为true。这里检查的区别在于我使用decltype(thing.count_)而不是decltype((thing.count_)). decltype当它直接通过成员访问表达式(不带括号)命名成员时会出现异常。在这种情况下,它将生成命名实体的类型,而不是表达式的类型。这验证了count_是 a int,而不是 a int&。
T如果是const- 限定的还是引用类型,您还应该考虑其他边缘情况。在这些情况下,您应该仔细考虑在哪些条件下应该满足该概念。根据答案,建议的解决方案可能足够,也可能不够。
作为另一个边缘情况,您需要考虑您是否真的想接受任何成员或仅接受非static成员。上述所有解决方案都假设您static也接受会员。
此外,您还需要考虑是否应该接受从基类继承的成员。上面所有建议的解决方案都接受它们。
所有上述解决方案还假设仅当成员是 时才应该被接受public。概念中的检查是从与任何类都不相关的上下文中完成的,因此基于上下文的可访问性无论如何都不起作用,并且可能没有理由接受private实际上在函数中不可用的成员受观念的限制。
| 归档时间: |
|
| 查看次数: |
1277 次 |
| 最近记录: |