I have some class Foo
and a std::list<std::reference_wrapper<Foo>>
and would like to iterate over its elements with a range-based for loop:
#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;
}
Notice the additional get()
when catching with auto
, as we deduce type std::reference_wrapper<Foo>
, whereas in the second case foo
is already implicitly converted to type Foo&
as we explicitly catch with this type.
I was actually looking for a way to catch with auto but implicitly cast away the std::reference_wrapper
implicitly in order to not have to bother with the get()
method all the time in the for
body, so I tried introducing a fitting concept and catching with this, i.e. I tried
//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;
}
and hoped that it would work. clang
however deduces the type of foo
to std::reference_wrapper<Foo>
, so that in fact below code will be correct:
//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;
}
However, gcc
completely refuses to accept the range-based for loop and complains deduced initializer does not satisfy placeholder constraints
, as it tries to check LikeFoo<std::reference_wrapper<Foo>>
, which of course evaluates to false, so with gcc
one cannot even catch foo
concept-restricted. Two questions arise:
- Which of the compilers is correct? Should
LikeFoo auto& foo : refs
be valid? - Is there a way to auto-catch (possibly concept-restricted)
foo : refs
such that one can avoid having to writeget()
in thefor
-loop body?
You can find this example at the Compiler explorer.