C ++ 11 범위 기반 for 루프에서 요소의 위치를 찾으십니까?
다음 코드가 있다고 가정합니다.
vector<int> list;
for(auto& elem:list) {
int i = elem;
}
elem
별도의 반복기를 유지하지 않고 벡터에서 의 위치를 찾을 수 있습니까 ?
예, 할 수 있습니다.
비결은 컴포지션을 사용하는 것입니다. 컨테이너를 직접 반복하는 대신 인덱스를 사용하여 "압축"합니다.
전문 지퍼 코드 :
template <typename T>
struct iterator_extractor { typedef typename T::iterator type; };
template <typename T>
struct iterator_extractor<T const> { typedef typename T::const_iterator type; };
template <typename T>
class Indexer {
public:
class iterator {
typedef typename iterator_extractor<T>::type inner_iterator;
typedef typename std::iterator_traits<inner_iterator>::reference inner_reference;
public:
typedef std::pair<size_t, inner_reference> reference;
iterator(inner_iterator it): _pos(0), _it(it) {}
reference operator*() const { return reference(_pos, *_it); }
iterator& operator++() { ++_pos; ++_it; return *this; }
iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; }
bool operator==(iterator const& it) const { return _it == it._it; }
bool operator!=(iterator const& it) const { return !(*this == it); }
private:
size_t _pos;
inner_iterator _it;
};
Indexer(T& t): _container(t) {}
iterator begin() const { return iterator(_container.begin()); }
iterator end() const { return iterator(_container.end()); }
private:
T& _container;
}; // class Indexer
template <typename T>
Indexer<T> index(T& t) { return Indexer<T>(t); }
그리고 그것을 사용 :
#include <iostream>
#include <iterator>
#include <limits>
#include <vector>
// Zipper code here
int main() {
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9};
for (auto p: index(v)) {
std::cout << p.first << ": " << p.second << "\n";
}
}
ideone 에서 볼 수 있지만 for-range 루프 지원이 없기 때문에 덜 예쁘다.
편집하다:
Boost.Range를 더 자주 확인해야한다는 것을 기억했습니다. 불행히도 zip
범위는 없지만 perl : boost::adaptors::indexed
. 그러나 인덱스를 가져 오려면 반복기에 대한 액세스가 필요합니다. 수치심 : x
그렇지 않으면 counting_range
제네릭과 함께 zip
흥미로운 일을 할 수 있다고 확신합니다 ...
이상적인 세상에서 나는 상상할 것입니다.
int main() {
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9};
for (auto tuple: zip(iota(0), v)) {
std::cout << tuple.at<0>() << ": " << tuple.at<1>() << "\n";
}
}
로 zip
자동으로 참조 튜플의 범위로 뷰를 생성하고 iota(0)
단순히 "false"로 범위를 만드는 것을 시작에서 0
그냥 카운트 무한대 방향으로 (또는 아니라, 그 유형의 최대 ...).
jrok이 맞습니다 : 범위 기반 for 루프는 그 목적으로 설계되지 않았습니다.
그러나 귀하의 경우 vector
요소를 연속적으로 저장하기 때문에 포인터 산술을 사용하여 계산할 수 있습니다 (*)
vector<int> list;
for(auto& elem:list) {
int i = elem;
int pos = &elem-&list[0]; // pos contains the position in the vector
// also a &-operator overload proof alternative (thanks to ildjarn) :
// int pos = addressof(elem)-addressof(list[0]);
}
그러나 이것은 코드를 난독 화하고 더 취약하게 만들기 때문에 분명히 나쁜 습관입니다 (누군가 컨테이너 유형을 변경 &
하거나 연산자를 오버로드 하거나 'auto &'를 'auto'로 바꾸면 쉽게 깨집니다 . 디버깅하는 것이 행운입니다!)
참고 : C ++ 03에서는 벡터, C ++ 11 표준에서는 배열 및 문자열에 대해 연속성이 보장됩니다.
아니요, 당신은 할 수 없습니다 (적어도 노력 없이는 아닙니다). 요소의 위치가 필요한 경우 범위 기반을 사용하면 안됩니다. 가장 일반적인 경우에 대한 편의 도구 일뿐입니다. 각 요소에 대해 일부 코드를 실행합니다. 요소의 위치가 필요한 덜 일반적인 상황에서는 덜 편리한 일반 for
루프 를 사용해야합니다 .
C ++ 14를 지원하는 컴파일러가있는 경우 함수 스타일로 수행 할 수 있습니다.
#include <iostream>
#include <string>
#include <vector>
#include <functional>
template<typename T>
void for_enum(T& container, std::function<void(int, typename T::value_type&)> op)
{
int idx = 0;
for(auto& value : container)
op(idx++, value);
}
int main()
{
std::vector<std::string> sv {"hi", "there"};
for_enum(sv, [](auto i, auto v) {
std::cout << i << " " << v << std::endl;
});
}
clang 3.4 및 gcc 4.9에서 작동합니다 (4.8에서는 작동하지 않음). 둘 다 설정해야합니다 -std=c++1y
. C ++ 14가 필요한 이유 auto
는 람다 함수 의 매개 변수 때문입니다 .
@Matthieu의 답변에 따르면 언급 된 boost :: adaptors :: indexed를 사용하는 매우 우아한 솔루션이 있습니다 .
std::vector<std::string> strings{10, "Hello"};
int main(){
strings[5] = "World";
for(auto const& el: strings| boost::adaptors::indexed(0))
std::cout << el.index() << ": " << el.value() << std::endl;
}
이것은 언급 된 "이상적인 세계 솔루션"과 매우 유사하게 작동하며 매우 구문이 있고 간결합니다. el
이 경우 의 유형 은와 유사 boost::foobar<const std::string&, int>
하므로 참조를 처리하고 복사가 수행되지 않습니다. 엄청나게 효율적입니다 : https://godbolt.org/g/e4LMnJ (코드는 자신의 카운터 변수를 유지하는 것과 동일합니다.)
완전성을 위해 대안 :
size_t i = 0;
for(auto const& el: strings) {
std::cout << i << ": " << el << std::endl;
++i;
}
또는 벡터의 연속 속성을 사용합니다.
for(auto const& el: strings) {
size_t i = &el - &strings.front();
std::cout << i << ": " << el << std::endl;
}
첫 번째는 부스트 어댑터 버전 (최적)과 동일한 코드를 생성하고 마지막은 1 명령 더 길다 : https://godbolt.org/g/nEG8f9
참고 : 마지막 요소 만 알고 싶다면 다음을 사용할 수 있습니다.
for(auto const& el: strings) {
bool isLast = &el == &strings.back();
std::cout << isLast << ": " << el << std::endl;
}
이것은 모든 표준 컨테이너에서 작동하지만 auto&
/ auto const&
사용되어야 하지만 (위와 동일) 어쨌든 권장됩니다. 입력에 따라 이것은 매우 빠를 수도 있습니다 (특히 컴파일러가 벡터의 크기를 알고있는 경우).
일반 코드의 안전한 쪽이되도록 &foo
by std::addressof(foo)
를 대체하십시오 .
If you insist on using range based for, and to know index, it is pretty trivial to maintain index as shown below. I do not think there is a cleaner / simpler solution for range based for loops. But really why not use a standard for(;;)? That probably would make your intent and code the clearest.
vector<int> list;
int idx = 0;
for(auto& elem:list) {
int i = elem;
//TODO whatever made you want the idx
++idx;
}
There is a surprisingly simple way to do this
vector<int> list;
for(auto& elem:list) {
int i = (&elem-&*(list.begin()));
}
where i
will be your required index.
This takes advantage of the fact that C++ vectors are always contiguous.
I read from your comments that one reason you want to know the index is to know if the element is the first/last in the sequence. If so, you can do
for(auto& elem:list) {
// loop code ...
if(&elem == &*std::begin(list)){ ... special code for first element ... }
if(&elem == &*std::prev(std::end(list))){ ... special code for last element ... }
// if(&elem == &*std::rbegin(list)){... (C++14 only) special code for last element ...}
// loop code ...
}
EDIT: For example, this prints a container skipping a separator in the last element. Works for most containers I can imagine (including arrays), (online demo http://coliru.stacked-crooked.com/a/9bdce059abd87f91):
#include <iostream>
#include <vector>
#include <list>
#include <set>
using namespace std;
template<class Container>
void print(Container const& c){
for(auto& x:c){
std::cout << x;
if(&x != &*std::prev(std::end(c))) std::cout << ", "; // special code for last element
}
std::cout << std::endl;
}
int main() {
std::vector<double> v{1.,2.,3.};
print(v); // prints 1,2,3
std::list<double> l{1.,2.,3.};
print(l); // prints 1,2,3
std::initializer_list<double> i{1.,2.,3.};
print(i); // prints 1,2,3
std::set<double> s{1.,2.,3.};
print(s); // print 1,2,3
double a[3] = {1.,2.,3.}; // works for C-arrays as well
print(a); // print 1,2,3
}
Here's a macro-based solution that probably beats most others on simplicity, compile time, and code generation quality:
#include <iostream>
#define fori(i, ...) if(size_t i = -1) for(__VA_ARGS__) if(i++, true)
int main() {
fori(i, auto const & x : {"hello", "world", "!"}) {
std::cout << i << " " << x << std::endl;
}
}
Result:
$ g++ -o enumerate enumerate.cpp -std=c++11 && ./enumerate
0 hello
1 world
2 !
Tobias Widlund wrote a nice MIT licensed Python style header only enumerate (C++17 though):
Really nice to use:
std::vector<int> my_vector {1,3,3,7};
for(auto [i, my_element] : en::enumerate(my_vector))
{
// do stuff
}
If you want to avoid having to write an auxiliary function while having the index variable local to the loop, you can use a lambda with a mutable variable.:
int main() {
std::vector<char> values = {'a', 'b', 'c'};
std::for_each(begin(values), end(values), [i = size_t{}] (auto x) mutable {
std::cout << i << ' ' << x << '\n';
++i;
});
}
참고URL : https://stackoverflow.com/questions/10962290/find-position-of-element-in-c11-range-based-for-loop
'programing' 카테고리의 다른 글
부울 필드 인덱싱 (0) | 2020.11.02 |
---|---|
Python 속성은 어떻게 작동합니까? (0) | 2020.11.02 |
ng-click 함수에 바인딩 된 변수를 어떻게 전달할 수 있습니까? (0) | 2020.11.01 |
nodejs 여권 인증 토큰 (0) | 2020.11.01 |
Linq Select에서 튜플 만들기 (0) | 2020.11.01 |