I've been reading text material to hone my C++ skills and am having trouble with my iterator-string experiment. What I want to do is take a string, iterate through each element in the string, and output each element in the string. However, since I am learning how to use the arrow operator, the compiler is telling me I cannot access the member string::empty. Doesn't the variable aIter have type string::iterator?
#include <iostream>
#include <string>
using namespace std;
int main()
{
string sString("some string!");
for(auto aIter = sString.cbegin();
aIter != sString.cend() && !aIter->empty();
aIter++)
cout << *aIter << endl;
cout << endl;
return 0;
}
Error:
request for member 'empty' in '* aIter.__gnu_cxx::__normal_iterator<_Iterator, _Container>::operator-><const char*, std::basic_string<char> >()', which is of non-class type 'const char'|
You're iterating over the characters of a string. Characters don't have member functions, and have no concept of being "empty".
You probably want to simply remove the !aIter->empty() test, to iterate until you reach the end of the string. This will work even if the string is empty: the cbegin() iterator will equal the cend() iterator, so the loop will end immediately.
If you wanted to check whether the string is empty (which you don't need to do here), that would be sString.empty().
Related
I am using std::find to check a string isn't in std::vector<std::vector<string>>
Error:
no match for 'operator==' (operand types are 'std::vector<std::__cxx11::basic_string<char> >' and 'const char [6]')
Isn't it the type doesn't match?
vector< vector< string>>data;
if(find(data.begin(), data.end(), "START") == data.end()){
printf("Missing \"START\"\n");
return true;`
The reason for the error message has been well explained in the other answer. I would like to provide a solution to the problem.
As you are trying to find, if any of the std::string element in the vector of vector matches to "START", you could use standard algorithm std::any_of in combination with a unary predicate which returns std::find(vec.cbegin(), vec.cend(), str) != vec.cend(); where vec is the each rows of the vector of vectors. See a demo here
#include <algorithm>
#include <string>
#include <iostream>
#include <vector>
bool isFound(const std::vector<std::vector<std::string>>& data, const std::string &str)
{
const auto found_in_vector = [&str](const std::vector<std::string> & vec)-> bool {
return std::find(vec.cbegin(), vec.cend(), str) != vec.cend(); // if found
};
if (std::any_of(data.cbegin(), data.cend(), found_in_vector))
{
std::cout << "Found\n";
return true;
}
std::cout << "Missing \""<< str << " \"\n";
return false;
}
int main()
{
std::vector<std::vector<std::string>> data;
std::vector<std::string> test{ "START","test" };
data.emplace_back(test);
std::cout << std::boolalpha << isFound(data, std::string{ "START" } );
}
Yes and no. The error is triggered, because you've got a "vector of vectors of strings", i.e. there's one dimension too much. Define data using std::vector<std::string> instead and it will work.
But why does the error talk about a missing operators?
When you use std::find(), it's typically implemented as a macro or templated function, which does the actual work, rather than a precompiled runtime function somewhere in a library. This allows the compiler full optimization based on the actual types of your parameters.
What it actually does - since your container is a class - is trying to find a special member function, std::vector<std::vector<std::string>>::operator==(const char*). It's not directly implemented this way, typically a template instead, but that's not important here. The important fact is it won't find any version of operator==() with an argument that is somehow able to accept the string passed, either directly or through conversion. Reason for this is that your vector contains vectors, so the only valid argument would be another vector of strings.
I'm getting an error when I use the iterator member function "empty()" on vector of type int, but not vector of type string (using the correct terms to the best of my understanding).
vector of type int:
#include <iostream>
#include <string>
#include <vector>
int main()
{
vector<int> ivec = {0,1,2};
auto iter = ivec.begin();
if(!iter->empty())
cout << "not empty" << endl;
return 0;
}
Output:
error C2839: invalid return type 'int *' for overload 'operator ->'
error C2039: 'empty': is not a member of 'std::Vector_iterator....'
vector of type string:
#include <iostream>
#include <string>
#include <vector>
int main()
{
vector<string> svec = {"text"};
auto iter = svec.begin();
if(!iter->empty())
cout << "not empty" << endl;
return 0;
}
Output:
not empty
Update
I now understand that depending on the type that is in the vector can you only perform certain operations.
You are not performing the operations on the iterator itself (because it is a pointer to the element in the container), but on the object that the iterator points to.
empty() is not "an iterator member function". You are not calling empty() on the iterator - you are dereferencing the iterator, and calling empty() on the vector element that the iterator refers to. It might be clearer if you replace iter->empty() with an equivalent form (*iter).empty()
In still other words,
auto iter = vec.begin();
iter->empty();
is equivalent to
vec[0].empty();
Here, iterators are not involved at all; hopefully, this would make the point extra clear.
Now, it so happens that std::string does have a member function empty(), while int of course does not (seeing as it's not a class type and so can't have any member functions). That's why your code compiles with vector<string> but not vector<int>.
I'm trying to use std::map::operator[] to iterate over the values of a std::map with a range-based for loop, but the following doesn't compile:
#include <iostream> // cout, endl
#include <map> // map
#include <set> // set
using namespace std;
int main () {
using namespace std;
const map<int, set<int>> m {{2, {201, 202}}, {3, {301, 302}}};
for (int v : m[2])
cout << v << endl;
return 0;
}
Here's the compiler's error message:
Test.c++:18:19: error: no viable overloaded operator[] for type 'const map<int, set<int> >'
for (int v : m[2])
The followup question is, Given that there are two versions of at(), why aren't there two versions of []?
map::operator[] inserts a new element into the map if the key isn't found, hence it cannot be const, and cannot be called on a const map.
Use m.at(2) instead.
operator[] will do one of two things. It will find the element at that location if it exists and return it. If there is no element at that location, it will value-initialize it, and then return it.
As such, it is neither logically nor programmatically const.
While you may say "but there is an element 2", the constness of an operation depends only on the types of the arguments, not the value of the arguments. And m[int] isn't guaranteed to have a valid element there.
To fix this, you can replace m[2] with m.find(2)->second. This does undefined behavior if 2 is missing from the map, but if 2 is present it will evaluate to the same thing as m[2]. Alterantively, m.at(2) will again do the same thing if 2 is present as a key, and throw an exception if is not present.
As an aside, when working on such a problem, try breaking your code down into smaller pieces. m[2]; all by itself on a line will fail to compile. In fact, the error message told you that it could not find an operator[] -- that might have given you a clue what was wrong.
Your map m is declared const meaning that you can only call const functions on it.
std:map::operator[] is not a such function, since it will modify the map if the specified key is not found.
What you need is std::map::at, which is const so you can call it on your const map.
I'm kind of confused with vectors in C++; this is my first time using them. I made a vector of strings, and I am trying to compare elements in that vector against a letter.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
/* Head ends here */
void displayPathtoPrincess(int n, vector <string> grid){
for(vector<string>::iterator it = grid.begin(); it != grid.end(); ++it) {
if(*it.strcompare('p') != 0)
cout << 'Princess found!';
}
}
/* Tail starts here */
int main() {
int m;
vector <string> grid;
cin >> m;
for(int i=0; i<m; i++) {
string s; cin >> s;
grid.push_back(s);
}
displayPathtoPrincess(m,grid);
return 0;
}
Why won't this work? Won't *it always be a string type?
Here is my error:
error: 'std::vector >::iterator' has no member named 'strcompare'
First, in this context, . binds tighter than *, so this
*it.strcompare('p')
is equivalent to
*(it.strcompare('p'))
so you should call something like
it->methodName(args)
Second, you need to call a method of std::string, presumably std::string::compare.
Note that if all you want to do is search for an entry equal to "p", then all you have to do is
auto it = std::find(grid.begin(), grid.end(), "p");
if (it != v.end()) std::cout << "Princess found!" << std::endl;
The class std::string is basic_string ( http://en.cppreference.com/w/cpp/string/basic_string ) and I do not think this class has a standard strcompare member function(Though some compilers do provide one).
I think you are trying to do something like:
if(*it == String("P"))
Which can more simply be written as:
if(*it == "P")
for an example: http://ideone.com/isa9Il
There is no strcompare in the Standard Library.
Perhaps you meant compare?
Also, yes *it will be a string -- but 'p' is not. That is a char. You need to either convert 'p' to a string (eg string s(1,'p')) or compare the first (or last) character in *it to 'p' (eg (*it)[0] == 'p' -- warning, unsafe as is)
Finally, others have already mentioned this, but *it.strcompare would not bind as (*it).strcompare, but rather as *(it.strcompare). Meaning, you are trying to call a method called strcompare on the iterator, rather than what the iterator refers to. Even if string had a method called strcompare (which it doesn't), your code still wouldn't compile.
I have a map defined like this
std::map<some_key_type, std::string::iterator> mIteratorMap;
And a huge string named "mHugeString". Then I walk trough the string collecting iterators like this:
std::string::iterator It=mHugeString.begin();
std::string::iterator EndIt=mHugeString.end();
for(;It!=EndIt;++It){
...defining a key element...
if(need_to_store_an_iterator)mIteratorMap[key_of_a_right_type]=It;
}
In the end I should recieve a map, where an iterator is associated with a some sort of key. But the iterator somehow looses itself when being paired with a key by "make_pair", unless it points to a place somewhere in the end of a string. It's hard to tell, but maybe last 256 bytes are fine.
So the question is not how to avoid loosing iterators, it was a stupid idea to store them anyways, but why trying to store an iterator in the begining of the string fails, and why the same with the iterators on the end works fine? What is the difference between them?
I haven't tried it but I would have expected that, of course you can store iterator values as values in a map.
Do you know that if you change the contents of mHugeString then any iterators into it which you have previously stored are now invalid?
You might choose to store the index into the string, instead of the iterator.
I am not sure why it should be problem. I wrote this code to check storage and retrieval of iterator which seems to work fine. [Note: I am not using make_pair as it should not be relevant, give try without using it though!]
#include <string>
#include <iostream>
#include <map>
enum UniqueKey {One, Two, Three};
typedef std::map<UniqueKey, std::string::iterator> UniqueKeyStringMap;
int main()
{
UniqueKeyStringMap storage;
std::string stringOne = "This is one string";
std::string::iterator it = stringOne.begin() + 8; // "o"
std::cout << " Iterator to store: " << std::string(it, it + 3) << std::endl;
storage[UniqueKey::One] = it; // Store iterator
// Retrieve and print, string and map are valid
UniqueKeyStringMap::iterator mapIter = storage.find(UniqueKey::One);
if (mapIter != storage.end())
{
std::cout << " From storage: " <<
std::string(mapIter->second, mapIter->second + 3) << std::endl;
}
}
expected output:
Iterator to store: one
From storage: one
It's fine, you can store iterators in the map. If you get some error, that is caused by something else. Note that if you modify your string, iterators pointing into your string will become invalid.
Please show us a complete, compilable code snippet that is rendered unusable, so we can analyze it.