我有课Foo
和一个 std::list<std::reference_wrapper<Foo>>
并希望使用基于范围的 for 循环迭代其元素:
#include <list>
#include <functional>
#include <iostream>
class Foo {
public:
Foo(int a) : a(a) {}
int a;
};
int main() {
std::list<Foo> ls = {{1},{2},{3},{4}};
std::list<std::reference_wrapper<Foo>> refs(ls.begin(), std::next(ls.begin(),2));
for(auto &foo : refs) {
std::cout << foo.get().a << std::endl;
}
for(Foo &foo : refs) {
std::cout << foo.a << std::endl;
}
return 0;
}
注意附加 get()
catch 时 auto
,正如我们推断类型 std::reference_wrapper<Foo>
,而在第二种情况下 foo
已经隐式转换为类型 Foo&
因为我们明确地捕获了这种类型。我实际上是在寻找一种方法来捕捉 auto 但隐含地抛弃了
std::reference_wrapper
隐含地为了不必打扰 get()
方法一直在for
body ,所以我尝试引入一个合适的概念并捕获这个,即我试过//this is not legal code
template<typename T>
concept LikeFoo = requires (T t) {
{ t.a };
};
int main() {
std::list<Foo> ls = {{1},{2},{3},{4}};
std::list<std::reference_wrapper<Foo>> refs(ls.begin(), std::next(ls.begin(),2));
for(LikeFoo auto &foo : refs) {
std::cout << foo.a << std::endl;
}
return 0;
}
并希望它能奏效。 clang
然而推导出foo
的类型至 std::reference_wrapper<Foo>
,因此实际上下面的代码将是正确的://this compiles with clang, but not with gcc
template<typename T>
concept LikeFoo = requires (T t) {
{ t.a };
};
int main() {
std::list<Foo> ls = {{1},{2},{3},{4}};
std::list<std::reference_wrapper<Foo>> refs(ls.begin(), std::next(ls.begin(),2));
for(LikeFoo auto &foo : refs) {
std::cout << foo.get().a << std::endl;
}
return 0;
}
然而,gcc
完全拒绝接受基于范围的 for 循环并提示 deduced initializer does not satisfy placeholder constraints
,因为它试图检查 LikeFoo<std::reference_wrapper<Foo>>
,当然评估为假,所以 gcc
一个人都抓不到foo
概念限制。出现两个问题:LikeFoo auto& foo : refs
有效吗? foo : refs
这样就可以避免必须写get()
在 for
-循环体? 您可以在 Compiler explorer 上找到此示例。 .
最佳答案
Which of the compilers is correct? Should
LikeFoo auto& foo : refs
be valid?
号
refs
是一个范围 reference_wrapper<Foo>&
, 所以 foo
推导出对 reference_wrapper<Foo>
的引用- 没有名为 a
的成员.约束变量声明不会改变推导的工作方式,它只是有效地表现得像一个额外的 static_assert
.Is there a way to auto-catch (possibly concept-restricted)
foo : refs
such that one can avoid having to writeget()
in the for-loop body?
只需写信
refs
?不。但是你可以写一个范围适配器来转换你的范围 reference_wrapper<T>
范围为 T&
.标准库中已经有这样的东西了,transform
:for (auto &foo : refs | std::views::transform([](auto r) -> decltype(auto) { return r.get(); })) {
这是一口,所以我们可以让它自己命名适配器:inline constexpr auto unwrap_ref = std::views::transform(
[]<typename T>(std::reference_wrapper<T> ref) -> T& { return ref; });
然后你可以写:for (auto &foo : refs | unwrap_ref) { ... }
for (auto &foo : unwrap_ref(refs)) { ... }
无论哪种方式,foo
这里推断为 Foo
.再多做一点工作,您就可以编写一个范围适配器来解包
reference_wrapper<T>
但保留任何其他引用类型。
关于c++ - std::list<std::reference_wrapper<T>> 的基于概念限制范围的 for 循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68610246/