Let's say I have several objects declared locally that I want to iterate over using range-based for syntax. This seems to work well, however, it appears that to put the local objects into the initializer_list, a copy is performed. This is bad news for objects like std::shared_ptr
for which (as I understand) incrementing the reference count is an atomic operation. The only way I think this can be avoided is by using raw pointers.
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> ptrInt1 = std::make_shared<int>(1);
std::shared_ptr<int> ptrInt2 = std::make_shared<int>(2);
/* in this loop, ptrInt1 and ptrInt2 are copied before they are binded
to ptrInt, this is ugly since the reference counter needs to temporarily
increased */
for(const std::shared_ptr<int>& ptrInt : {ptrInt1, ptrInt2}) {
std::cerr << *ptrInt << std::endl;
}
/* this solution works, but it feels somewhat ugly having to convert my smart
pointers to raw pointers to avoid the copying, perhaps there is a better
solution ?? */
for(const int* rawPtrInt : {ptrInt1.get(), ptrInt2.get()}) {
std::cerr << *rawPtrInt << std::endl;
}
return 0;
}
Is there a way to iterate over a group of locally declared objects without copying them or resorting to using raw pointers?
You can use std::ref
to build a list of std::reference_wrapper
s. This hides the pointer and lets you write the list like
for(const std::shared_ptr<int>& ptrInt : {std::ref(ptrInt1), std::ref(ptrInt2)}) {
std::cerr << *ptrInt << std::endl;
}
Here's a small function template that extends @NathanOliver's answer and reduces some of the typing.
#include <array>
#include <functional>
// No rvalues, thanks to @NathanOliver for pointing that out:
template <class ...T>
auto crefRange(const T&&...) = delete;
template <class ...T>
auto crefRange(const T&... args)
{
using First = std::tuple_element_t<0, std::tuple<T...>>;
return std::array<First, sizeof...(T)>{{std::cref(args)...}};
}
You can instantiate and call it via
for(const std::shared_ptr<int>& ptrInt : crefRange(ptrInt1, ptrInt2))
std::cerr << *ptrInt << std::endl;
This will fail if you instantiate it with different types, but this limitation is identical with the std::initializer_list
approach.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With