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 : refsbe valid? - Is there a way to auto-catch (possibly concept-restricted)
foo : refssuch that one can avoid having to writeget()in thefor-loop body?
You can find this example at the Compiler explorer.
autowill always deduce the actual type. In order to get aFoo&an implicit conversion is needed. A concepts is a constraint on what type is allowed to be deduced. It can't apply conversions.refsin something that will automaticallyget()each element for you. Butfor (auto& foo : unwrap_reference_wrapper(refs))just looks like a more convoluted version offor (Foo& : refs). It could have it's place in generic code though.