61

My code is as follows:

std::cin >> str;
for ( char c : str )
    if ( c == 'b' ) vector.push_back(i) //while i is the index of c in str

Is this doable? Or I will have to go with the old-school for loop?

jogojapan
  • 68,383
  • 11
  • 101
  • 131
Shane Hsu
  • 7,937
  • 6
  • 39
  • 63
  • 1
    Almost the same: http://stackoverflow.com/questions/10962290/find-position-of-element-in-c11-range-based-for-loop – jogojapan Mar 01 '13 at 02:50
  • 1
    Also related: http://stackoverflow.com/questions/9005835/range-based-for-in-c11 – jogojapan Mar 01 '13 at 02:54
  • 2
    Just want to add, for a 9 yo question, this does not age well. There are many modern improvements. Despite views::enumerate delayed until C++26, we still now have ranges and views that can emulates in C++23, so when C++23 and the libraries got implemented, we can do this: https://godbolt.org/z/dx8xs5G15 (or you know, use libs) – Shane Hsu Apr 17 '22 at 06:35

6 Answers6

48

Maybe it's enough to have a variable i?

unsigned i = 0;
for ( char c : str ) {
  if ( c == 'b' ) vector.push_back(i);
  ++i;
}

That way you don't have to change the range-based loop.

Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • 3
    It's a solution, but it's not elegant. Also, I think it will be pointless as it's implementing traditional for loop in a range-based one. – Shane Hsu Mar 01 '13 at 02:31
  • 17
    Maybe it's not elegant, but it's the baseline wrt complexity, readability, overhead, etc. for all other upcoming solutions/work-arounds. – Daniel Frey Mar 01 '13 at 02:34
  • 15
    Sure. But I really think there should be a constant `index` in C++ Range-Based for loop. – Shane Hsu Mar 01 '13 at 02:42
  • 5
    You asked about the current language status, where something like `index` does not exist. Whether and how the language could be extended is a different question and does not belong here. – Daniel Frey Mar 01 '13 at 02:45
  • 4
    One problem with this - the i variable is scoped outside the loop. In case of the traditional for, there is a place where you can declare extra variables, when they are all of the same type (as your main 'loop variable'). – Tomasz Gandor Feb 20 '17 at 07:38
  • 3
    Note that C++20 will allow declaration of `i` in the `for` statement: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0614r1.html . @TomaszGandor for that, you'd encapsulate the `for` loop with the declaration of `I` in braces. – zeeMonkeez Mar 14 '19 at 07:55
  • At last. All the excuses about this mutant code being correct are BS. – Pablo Ariel Oct 11 '22 at 07:15
45

Assuming str is a std::string or other object with contiguous storage:

std::cin >> str;
for (char& c : str)
    if (c == 'b') v.push_back(&c - &str[0]);
Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
  • Nice! I was thinking that vectors can use something like `it - vector.begin()`, and you solved my problem! Thanks! – Shane Hsu Mar 01 '13 at 03:28
  • 1
    Thanks! I got `&it - &vector[0]` to compile. But is there a nice way such as @ShaneHsu 's comment? I figured out, I had to use `&it - vector.begin()` using iterators such as `const initializer_list& it`. However, in a loop such as `for(const pair& it : vector_of_pairs)` this yields an error: _no match for ‘operator-’ (operand types are ‘const pair*’ and vector >::const_iterator_ . How can I get the address of the iterator ? (Note that the former is in a ctor, whereas the latter is in a const method) – BadAtLaTeX Jan 09 '16 at 17:17
  • Note if the underlying storage changes into no longer a vector type, it will break. Likely your integer based semantics would also then break... so this isnt a strong point against doing this. But it does feel like you'd be strengthening your commitment to contiguous storage by doing this. – Steven Lu Mar 05 '18 at 23:23
  • @gr4nt3d your question appears to be about how you cannot obtain an ordering out of an iterator for an associative type, which (say it's a hashtable) pretty much *has no ordering*, you can only do stuff like check its equality to `container.end()`, or fetch the next one, etc. See https://stackoverflow.com/a/43680932/340947 – Steven Lu Mar 05 '18 at 23:28
  • @StevenLu, I don't get your point; I was referring to plain `std::vector` (at least telling from the comment, as it's been quite some time since then). As such it would have the ordering that I need, but apparently the expression `&it - vector_of_pairs.begin()`did not work even though I'd expect the pointer arithmetic to work, as it is contiguously stored. However, I cannot recall the exact case anymore. – BadAtLaTeX Apr 02 '18 at 22:55
  • The difference seems to be between getting the address of the first element of the vector Vs. getting the begin() iterator. I’m not too sure on why or what exactly the limitations are but direct arithmetic on iterators seems to not work – Steven Lu Apr 03 '18 at 14:11
17

In C++ 20, I use initializer like this:

for(unsigned short i = 0; string item : nilai){
   cout << i << "." << "address " << &item << " -> " << item << endl;
   i++;
}

So, your case will be like:

for (unsigned short i = 0; char c : str ) {
  if ( c == 'b' ) vector.push_back(i);
  ++i;
}

I don't know what 'vector' mean in your case, and what is push_back(). Don't forget to add -std=c++20 (I just use g++ for compiling, so i don't know much about other compiler). You can also start the 'i' value from 1 if you want to. I think it's elegant enough

Enkracken
  • 181
  • 2
  • 6
9

The range loop will not give you the index. It is meant to abstract away such concepts, and just let you iterate through the collection.

Karthik T
  • 31,456
  • 5
  • 68
  • 87
6

What you are describing is known as an 'each with index' operation in other languages. Doing some quick googling, it seems that other than the 'old-school for loop', you have some rather complicated solutions involving C++0x lambas or possibly some Boost provided gems.

EDIT: As an example, see this question

Community
  • 1
  • 1
3

You can use lambdas in c++11:

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <iterator>

using namespace std;


int main() {
    std::string str;
    std::vector<char> v;
    auto inserter = std::back_insert_iterator<decltype(v)>(v);

    std::cin >> str;
    //If you don't want to read from input
    //str = "aaaaabcdecccccddddbb";

    std::copy_if(str.begin(), str.end(), inserter, [](const char c){return c == 'b';});

    std::copy(v.begin(),v.end(),std::ostream_iterator<char>(std::cout,","));

    std::cout << "Done" << std::endl;

}
jogojapan
  • 68,383
  • 11
  • 101
  • 131
Joel
  • 2,928
  • 2
  • 24
  • 34
  • 1
    It sure seems complicated, but it's definitely worth learning! Thank! I will look into it. – Shane Hsu Mar 01 '13 at 02:39
  • 4
    This does not answer the question. The OP wanted the numeric index of the instances of 'b', not the instances themselves. – mskfisher Feb 04 '15 at 19:47