68

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?

3

7 Answers 7

50

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.

Sign up to request clarification or add additional context in comments.

7 Comments

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.
Maybe it's not elegant, but it's the baseline wrt complexity, readability, overhead, etc. for all other upcoming solutions/work-arounds.
Sure. But I really think there should be a constant index in C++ Range-Based for loop.
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.
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').
|
44

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]);

7 Comments

Nice! I was thinking that vectors can use something like it - vector.begin(), and you solved my problem! Thanks!
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<double>& it. However, in a loop such as for(const pair<double, int>& it : vector_of_pairs) this yields an error: no match for ‘operator-’ (operand types are ‘const pair<double, int>*’ and vector<pair<double, int> >::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)
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.
@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 stackoverflow.com/a/43680932/340947
@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.
|
31

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

Comments

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.

Comments

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

Comments

5

Since C++23 there is a way combining iota_view and zip_view:

std::cin >> str;

for (const auto [i, value] : std::views::zip(std::ranges::iota_view(0), str)) {
    if (value == 'b') {
        vector.push_back(i);
    }
}

1 Comment

Agreed with Jade's edition, just prefer to inline iota_view(0) since it better answer to initial request
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;

}

2 Comments

It sure seems complicated, but it's definitely worth learning! Thank! I will look into it.
This does not answer the question. The OP wanted the numeric index of the instances of 'b', not the instances themselves.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.