I try to generalize for stream objects with following code:
#include <iostream>
#include <vector>
#include <sstream>
#include <iterator>
using namespace std;
template<class T, class U>
T& operator<<(T& os, vector<U> vec)
{
vector<string>::iterator begin = vec.begin();
vector<string>::iterator end = vec.end();
for (; begin != end; ++begin)
os << *begin << " ";
return os;
}
int main()
{
vector<string> things({
"car", "truck", "rabbit"
});
ostringstream oss;
oss << things << endl;
copy(oss.str().begin(), oss.str().end(), ostream_iterator<char>(cout, ""));
}
Now it work with cout << things and string str = oss.str(); copy(str.begin() ... but not with oss.str().begin(). From what I understand, str() returns a string object with a copy of the current contents of the stream. So why do we need to copy it twice, once from str() and once in initialization of string object? This question is not same as using c_str().
Following also works:
string::iterator begin = oss.str().begin();
string::iterator end = oss.str().end();
copy(begin, end, ostream_iterator<char>(cout, ""));
From what I understand, str() returns a string object with a copy of
the current contents of the stream.
That is correct. And it is the answer to your question. Because str() returns a copy, two calls to oss.str() result in two completely different copies (i.e. two different string objects). The iterators of the two respective string objects are not compatible, since they are not from the same string. It is undefined behavior to pass two iterators from two completely different strings to an algorithm (such as std::copy) which is expecting them to be from the same range.
You wouldn't expect this to work, would you?
std::string str1 = oss.str();
std::string str2 = oss.str();
std::copy(str1.begin(), str2.end(), ostream_iterator<char>(cout, ""));
In response to your edit, "Following also works:". If that works, it is purely by coincidence. Those iterators are both invalid at the end of the statement in which they were created, because the string into which they point no longer exists. Your use of them in the call to std::copy is undefined behavior.
Related
For simplification of my question i will use std::string::iterator and std::string::reverse_iterator but the question is about iterators in general.
Is there any particular reason to iterate backwards using the following loop:
std::string s = "something";
for (std::string::reverse_iterator it = s.rbegin(); it != s.rend(); ++it)
rather than this one:
std::string s = "something";
std::string::iterator it = in.end();
while(it!=in.begin())
{
it--;
//do something
}
Reverse iterators allow you to reuse generic code because you can treat them like normal iterators, calling ++ to go backwards. For example:
#include <iostream>
#include <string>
template <class Iterator>
void printAll(Iterator begin, Iterator end)
{
for (auto it = begin; it != end; ++it) // ++ can mean "go backwards"
// if Iterator is a reverse
// iterator
{
std::cout << *it << "\n";
}
}
int main()
{
std::string s = "123";
printAll(s.begin(), s.end()); // prints 1, 2, 3
printAll(s.rbegin(), s.rend()); // prints 3, 2, 1
}
Notice how you do not need to write a reverse version for printAll using --.
Now, consider all the functions in <algorithm>. The existence of reverse iterators means that you can easily use all of them in a reverse manner. For example, there is std::copy_n, but not std::reverse_copy_n, but with reverse iterators, it's not necessary, because you can write something like this:
#include <iostream>
#include <string>
#include <algorithm>
#include <iterator>
int main()
{
std::string input = "abcdef";
std::string output;
std::string output_reversed;
// copy the first 3 elements:
std::copy_n(input.begin(), 3, std::back_inserter(output));
// copy the first 3 elements going backwards, starting at the last one:
std::copy_n(input.rbegin(), 3, std::back_inserter(output_reversed));
std::cout << output << "\n"; // prints abc
std::cout << output_reversed << "\n"; // prints fed
}
For non-generic code, such as in your question, it's more of a style issue, with few technically sound arguments to prefer one over the other.
Since begin() points to the first member, and end() points to the member next to the last, that is all about clean code (since in case of using not reverse iterators you would do iterator decrement first, then the code you want to execute, then you will compare iterator with begin(), but it is wrong, since begin() points to an existing first element.
std::vector::end() at cplusplus.com
I am looking for a standard library equivalent of this code for accumulating elements of an std container into a string with a delimiter separating consecutive entries:
string accumulate_with_delimiter( vector<string> strvect, string delimiter )
{
string answer;
for( vector<string>::const_iterator it = strvect.begin(); it != strvect.end(); ++it )
{
answer += *it;
if( it + 1 != strvect.end() )
{
answer += delimiter;
}
}
return answer;
}
Such code seems to be very common: printing out an array with delimiter " ", or saving into a CSV file with delimiter ",", etc. Therefore it's likely that a piece of code like that made its way into a standard library. std::accumulate comes close, but doesn't have a delimiter.
I don't think the standard C++ library has a nice approach to delimiting sequences. I typically end up using something like
std::ostringstream out;
if (!container.empty()) {
auto end(container.end());
std::copy(container.begin(), --end, std::ostream_iterator<T>(out, ", "));
out << *end;
}
Using std::accumulate() has a similar problem of although with the first element rather than the last element. Using a custom add function, you could use it something like this:
std::string concat;
if (!container.empty()) {
auto begin(container.begin());
concat = std::accumulate(++begin, container.end(), container.front(),
[](std::string f, std::string s) { return f + ", " + s; });
}
In both cases the iterators need to be moved to another element. The code uses temporary objects when moving the iterator because the container may use pointers as iterator in which case a pre-increment or pre-decrement on the result from begin() or end() doesn't work.
std::accumulate might be the correct answer, but you need the version which takes a custom adder. You can then provide your own lambda.
Remember to pass front() as the first value to accumulate, and start adding at begin() + 1. And test for empty vectors first of course.
I'm not sure if there is one in the recent Standard Library or not, but there is always boost::algorithm::join(strvec, delimiter).
So if I have a vector of words like:
Vec1 = "words", "words", "are", "fun", "fun"
resulting list: "fun", "words"
I am trying to determine which words are duplicated, and return an alphabetized vector of 1 copy of them. My problem is that I don't even know where to start, the only thing close to it I found was std::unique_copy which doesn't exactly do what I need. And specifically, I am inputting a std::vector<std::string> but outputting a std::list<std::string>. And if needed, I can use functor.
Could someone at least push me in the right direction please? I already tried reading stl documentation,but I am just "brain" blocked right now.
In 3 lines (not counting the vector and list creation nor the superfluous line-breaks in name of readability):
vector<string> vec{"words", "words", "are", "fun", "fun"};
list<string> output;
sort(vec.begin(), vec.end());
set<string> uvec(vec.begin(), vec.end());
set_difference(vec.begin(), vec.end(),
uvec.begin(), uvec.end(),
back_inserter(output));
EDIT
Explanation of the solution:
Sorting the vector is needed in order to use set_difference() later.
The uvec set will automatically keep elements sorted, and eliminate duplicates.
The output list will be populated by the elements of vec - uvec.
Make an empty std::unordered_set<std::string>
Iterator your vector, checking whether each item is a member of the set
If it's already in the set, this is a duplicate, so add to your result list
Otherwise, add to the set.
Since you want each duplicate only listed once in the results, you can use a hashset (not list) for the results as well.
IMO, Ben Voigt started with a good basic idea, but I would caution against taking his wording too literally.
In particular, I dislike the idea of searching for the string in the set, then adding it to your set if it's not present, and adding it to the output if it was present. This basically means every time we encounter a new word, we search our set of existing words twice, once to check whether a word is present, and again to insert it because it wasn't. Most of that searching will be essentially identical -- unless some other thread mutates the structure in the interim (which could give a race condition).
Instead, I'd start by trying to add it to the set of words you've seen. That returns a pair<iterator, bool>, with the bool set to true if and only if the value was inserted -- i.e., was not previously present. That lets us consolidate the search for an existing string and the insertion of the new string together into a single insert:
while (input >> word)
if (!(existing.insert(word)).second)
output.insert(word);
This also cleans up the flow enough that it's pretty easy to turn the test into a functor that we can then use with std::remove_copy_if to produce our results quite directly:
#include <set>
#include <iterator>
#include <algorithm>
#include <string>
#include <vector>
#include <iostream>
class show_copies {
std::set<std::string> existing;
public:
bool operator()(std::string const &in) {
return existing.insert(in).second;
}
};
int main() {
std::vector<std::string> words{ "words", "words", "are", "fun", "fun" };
std::set<std::string> result;
std::remove_copy_if(words.begin(), words.end(),
std::inserter(result, result.end()), show_copies());
for (auto const &s : result)
std::cout << s << "\n";
}
Depending on whether I cared more about code simplicity or execution speed, I might use an std::vector instead of the set for result, and use std::sort followed by std::unique_copy to produce the final result. In such a case I'd probably also replace the std::set inside of show_copies with an std::unordered_set instead:
#include <unordered_set>
#include <iterator>
#include <algorithm>
#include <string>
#include <vector>
#include <iostream>
class show_copies {
std::unordered_set<std::string> existing;
public:
bool operator()(std::string const &in) {
return existing.insert(in).second;
}
};
int main() {
std::vector<std::string> words{ "words", "words", "are", "fun", "fun" };
std::vector<std::string> intermediate;
std::remove_copy_if(words.begin(), words.end(),
std::back_inserter(intermediate), show_copies());
std::sort(intermediate.begin(), intermediate.end());
std::unique_copy(intermediate.begin(), intermediate.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}
This is marginally more complex (one whole line longer!) but likely to be substantially faster when/if the number of words gets very large. Also note that I'm using std::unique_copy primarily to produce visible output. If you just want the result in a collection, you can use the standard unique/erase idiom to get unique items in intermediate.
In place (no additional storage). No string copying (except to result list). One sort + one pass:
#include <string>
#include <vector>
#include <list>
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
vector<string> vec{"words", "words", "are", "fun", "fun"};
list<string> dup;
sort(vec.begin(), vec.end());
const string empty{""};
const string* prev_p = ∅
for(const string& s: vec) {
if (*prev_p==s) dup.push_back(s);
prev_p = &s;
}
for(auto& w: dup) cout << w << ' ';
cout << '\n';
}
You can get a pretty clean implementation using a std::map to count the occurrences, and then relying on std::list::sort to sort the resulting list of words. For example:
std::list<std::string> duplicateWordList(const std::vector<std::string>& words) {
std::map<std::string, int> temp;
std::list<std::string> ret;
for (std::vector<std::string>::const_iterator iter = words.begin(); iter != words.end(); ++iter) {
temp[*iter] += 1;
// only add the word to our return list on the second copy
// (first copy doesn't count, third and later copies have already been handled)
if (temp[*iter] == 2) {
ret.push_back(*iter);
}
}
ret.sort();
return ret;
}
Using a std::map there seems a little wasteful, but it gets the job done.
Here's a better algorithm than the ones other people have proposed:
#include <algorithm>
#include <vector>
template<class It> It unique2(It const begin, It const end)
{
It i = begin;
if (i != end)
{
It j = i;
for (++j; j != end; ++j)
{
if (*i != *j)
{ using std::swap; swap(*++i, *j); }
}
++i;
}
return i;
}
int main()
{
std::vector<std::string> v;
v.push_back("words");
v.push_back("words");
v.push_back("are");
v.push_back("fun");
v.push_back("words");
v.push_back("fun");
v.push_back("fun");
std::sort(v.begin(), v.end());
v.erase(v.begin(), unique2(v.begin(), v.end()));
std::sort(v.begin(), v.end());
v.erase(unique2(v.begin(), v.end()), v.end());
}
It's better because it only requires swap with no auxiliary vector for storage, which means it will behave optimally for earlier versions of C++, and it doesn't require elements to be copyable.
If you're more clever, I think you can avoid sorting the vector twice as well.
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.
This question already has answers here:
How do you reverse a string in place in C or C++?
(21 answers)
Closed 6 years ago.
Im trying to figure out how to reverse the string temp when I have the string read in binary numbers
istream& operator >>(istream& dat1d, binary& b1)
{
string temp;
dat1d >> temp;
}
I'm not sure what you mean by a string that contains binary numbers. But for reversing a string (or any STL-compatible container), you can use std::reverse(). std::reverse() operates in place, so you may want to make a copy of the string first:
#include <algorithm>
#include <iostream>
#include <string>
int main()
{
std::string foo("foo");
std::string copy(foo);
std::cout << foo << '\n' << copy << '\n';
std::reverse(copy.begin(), copy.end());
std::cout << foo << '\n' << copy << '\n';
}
Try
string reversed(temp.rbegin(), temp.rend());
EDIT: Elaborating as requested.
string::rbegin() and string::rend(), which stand for "reverse begin" and "reverse end" respectively, return reverse iterators into the string. These are objects supporting the standard iterator interface (operator* to dereference to an element, i.e. a character of the string, and operator++ to advance to the "next" element), such that rbegin() points to the last character of the string, rend() points to the first one, and advancing the iterator moves it to the previous character (this is what makes it a reverse iterator).
Finally, the constructor we are passing these iterators into is a string constructor of the form:
template <typename Iterator>
string(Iterator first, Iterator last);
which accepts a pair of iterators of any type denoting a range of characters, and initializes the string to that range of characters.