Why does a std::variant compile with begin and end iterators? - c++

It seems that the compiler should be able to catch the fact that std::variant doesn't have iterator methods, but it seems that my code compiles with no problem (even if I randomly make up methods or member variables for the variant), but it crashes at runtime (rightfully so). Can someone shed some light on why this code compiles?
Note: this isn't blocking progress because now I'm using std::visit, but it would be nice to know why this is compiling.
I have tried using different variant patterns and they all compile. See code examples. You can pop this in to cppreferences, or godbolt and it should compile with C++17 flags or greater
#include <variant>
#include <string>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
template<typename K, typename V>
//using var_maps = std::variant<std::map<K,V>, std::multimap<K,V> >;
//using var_maps = std::variant<std::list<int>, std::list<float> >;
using var_maps = std::variant<int, float>;
template <typename K, typename V>
void flat( const var_maps<K,V>& vmap)
{
//for(auto bIter = vmap.bexxxgin(), eIter = vmap.end(); bIter != eIter;
for(auto bIter = vmap.begin(), eIter = vmap.end(); bIter != eIter;
bIter = vmap.upper_bound( bIter->first ) )
{
}
}
My initial case was with maps, but it effectively compiles with anything. Additionally I can randomly replace begin() to any other word and it still compiles. I know the right way to do this is with visits. I am inevitably trying to have one function that handles both map and multimap and transform it to another data structure.
Thank you!

Your code compiles, because begin() and end() are dependent names - they depend on the function template arguments, so their lookup is postponed until the flat template instantiation. But it is never instantiated!
If you add the following, your code won't compile anymore:
int main () {
&flat<int, int>;
}

It "compiles" because the function is a template. No code is generated here and beyond basic syntax checking no complete checking can be done when the template is parsed.
This is because the compiler does not know whether the var_maps<K,V> contains begin() or not. There could be specializations.
You will receive the error when you instantiate var_maps, i.e. use var_maps with concrete types K and V.

Related

Any way to trick std::transform into operating on the iterator themselves?

So I wrote this code which won't compile. I think the reason is because std::transform, when given an iterator range such as this will operate on the type pointed to by the iterator, not the iterator itself. Is there any simple wrapper, standard lib tool, etc. to make this code work i.e. to store all the iterators of the original map into a new vector, with minimum changes required? Thanks!
#include <map>
#include <iostream>
#include <vector>
using MT = std::multimap<char, int>;
using MTI = MT::iterator;
int main()
{
MT m;
m.emplace('a', 1); m.emplace('a', 2); m.emplace('a', 3);
m.emplace('b', 101);
std::vector<MTI> itrs;
std::transform(m.begin(), m.end(), std::back_inserter(itrs), [](MTI itr){
return itr;
});
}
EDIT 1: Failed to compile with gcc11 and clang13, C++17/20
EDIT 2: The purpose of the question is mostly out of curiosity. I want to see what's a good way to manipulate existing standard algorithm to work on the level that I want. The sample code and problem are entirely made up for demonstration but they are not related to any real problem that requires a solution
Is there such a wrapper? Not in the standard. But it doesn't mean you can't write one, even fairly simply.
template<typename It>
struct PassIt : It {
It& operator*() { return *this; }
It const& operator*() const { return *this; }
PassIt & operator++() { ++static_cast<It&>(*this); return *this; }
PassIt operator++(int) const { return PassIt{static_cast<It&>(*this)++}; }
};
template<typename It>
PassIt(It) -> PassIt<It>;
That is just an example1 of wrapper that is a iterator of the specified template parameter type. It delegates to its base for the bookkeeping, while ensuring the the return types conform to returning the wrapped iterator itself when dereferencing.
You can use it in your example to simply copy the iterators
std::copy(PassIt{m.begin()}, PassIt{m.end()}, std::back_inserter(itrs));
See it live
(1) - It relies on std::iterator_traits deducing the correct things. As written in this example, it may not conform to all the requirements of the prescribed iterator type (in this case, we aimed at a forward iterator). If that happens, more boiler-plate will be required.
The function you pass to std::transform and algorithms in general are supposed to use elements not iterators. You could use the key to find the iterator in the map, though thats neither efficient nor simple. Instead use a plain loop:
for (auto it = m.begin(); it != m.end(); ++it) itrs.push_back(it);

Inferring a type from a dereferenced iterator in a template function

I am working through the exercises in "Accelerated C++", and I have discovered some behaviour I do not understand regarding how the compiler infers types in function templates. In exercise 10-2, we are asked to write a template function that can compute the median of a list of arithmetic types in a vector or a built-in array. I stumbled upon an example solution to this problem that involves a template function that computes and returns the median container value between two iterators, i.e. I created the following file called "median.hpp" :
#ifndef median_hpp
#define median_hpp
#include <algorithm>
#include <stdexcept>
#include <vector>
using std::domain_error;
using std::sort;
using std::vector;
template <class T, class Iterator>
T median(Iterator begin, Iterator end) {
//check if the container is empty
if (begin == end)
throw domain_error("median of an empty container");
//create a vector with the same type as the container
//and copy the container contents into it
vector<T> temp;
for ( ; begin != end; ++begin)
temp.push_back(*begin);
//sort the temporary vector, and compute and return the median
sort(temp.begin(), temp.end());
size_t mid = temp.size() / 2;
T ret = (temp.size() % 2 == 0)
? (temp[mid] + temp[mid - 1]) / 2
: temp[mid];
return ret;
}
#endif /* median_hpp */
so if I wanted to compute, say, the median value of an array and vector to demonstrate that this function works for both container types I would use the aforementioned template function like this:
#include <iostream>
#include <vector>
#include "median.hpp"
using std::vector;
using std::cout;
using std::cin;
using std::endl;
int main()
{
int arr[] = {12,2,4,1,4,56,1};
const size_t nData = sizeof(arr)/sizeof(*arr);
vector<double> v(arr, arr + nData);
cout << median(v.begin(),v.end()) << endl;
cout << median(arr, arr + nData) << endl;
return 0;
}
however, for reasons that I do not understand, I get the following error:
No matching function for call to 'median'... Candidate template ignored: couldn't infer template argument 'T'
As far as I can see, the problem is that the compiler is not able to infer the type of "T" from the dereferenced iterators. I would like to know
A. Why is this happening?
B. Is there an elegant way to solve this problem?
The compiler can infer the Iterator, but not T. That's because the oterator can't say how to infer T from anything you pass to it. Iterator? From the unknown Iterator type, how would you be able to know what T is without knowing what Iterator actually is? The compiler simply doesn't know that.
However, since you have knowledge that Iterator is an actual iterator type, and that most iterator have type-aliases back to the contained type T you could do something like
template <class Iterator, class T = typename std::iterator_traits<Iterator>::value_type>
T median(Iterator begin, Iterator end) { ... }
Information about all this can be gleaned from e.g. this std::vector reference which tells you that the iterator type in the vector is a random access iterator which mentions value_type and how it can be found from std::iterator_traits.
std::iterator_traits should be possible to use for all standard iterators, not only the random access iterators given by std::vector.
Since there is no argument of type T for median, the compiler cannot infer that type.
Solution:
template <class Iterator, class T = typename std::iterator_traits<Iterator>::value_type>
T median(Iterator begin, Iterator end) {
// ....
}
live example

Iterating Streams in Reverse

I would like to use std::find_if to traverse the contents of an std::streambuf in reverse. This involves constructing an std::reverse_iterator from an std::istream_iterator or std::istreambuf_iterator. Unfortunately, trying to do this, as shown in the code sample below, results in a compilation error. How can I get this to work? If necessary, solutions using Boost would be great.
#include <cstddef>
#include <fstream>
#include <iterator>
template <class Iterator>
static std::reverse_iterator<Iterator>
make_reverse_iterator(Iterator i)
{
return std::reverse_iterator<Iterator>(i);
}
int main()
{
std::ifstream is{"foo.txt", std::ios::binary};
std::istreambuf_iterator<char> i{is};
auto r = make_reverse_iterator(i);
// Error =(
*r;
return EXIT_SUCCESS;
}
Here is the compilation error reported by g++-4.8.1:
In file included from /opt/local/include/gcc48/c++/bits/stl_algobase.h:67:0,
from /opt/local/include/gcc48/c++/bits/char_traits.h:39,
from /opt/local/include/gcc48/c++/ios:40,
from /opt/local/include/gcc48/c++/istream:38,
from /opt/local/include/gcc48/c++/fstream:38,
from ri.cpp:9:
/opt/local/include/gcc48/c++/bits/stl_iterator.h: In instantiation of 'std::reverse_iterator<_Iterator>::reference std::reverse_iterator<_Iterator>::operator*() const [with _Iterator = std::istream_iterator<char>; std::reverse_iterator<_Iterator>::reference = const char&]':
ri.cpp:24:3: required from here
/opt/local/include/gcc48/c++/bits/stl_iterator.h:163:10: error: no match for 'operator--' (operand type is 'std::istream_iterator<char>')
return *--__tmp;
^
Thanks for your help!
As far as I know input iterators (such as those of ifstreams) are not capable of going backwards which is why the reverse iterator is not available. Which makes sense because if you think about it, the forward of the reverse_iterator (i.e. operator ++) is the backwards of the normal iterator (i.e. operator --) and so if the normal iterator doesn't provide operator --, then it stands to reason that the reverse_iterator should not exist.
As I recall there are 3 types of iterators: forward, bidirectional and random access. Forward can only go in one direction (guess which :P), bidirectional can go forward and backwards by 1 and random access can go forwards and backwards by whatever increment.
As you can see random access iterators offer all the operations of bidirectional iterators (and more) who themselves offer all the operations of forward iterators (and more). Which means random access iterators can be used where forward iterators are require but not the other way around.
As you may have guessed from this explanation make_reverse_iterator most likely requires either bidirectional or random access iterators and ifstream most likely offers only forward which is why the template instantiation fails.

boost::range_iterator and boost::iterator_range confusion

I have been going through the boost::range library and noticed boost::range_iterator and boost::iterator_range. I am confused with these terms here. Could anyone please explain what is the difference between two and when to use what? Also, it would be nice if you can point me to sample examples where the boost range library is used to know more about it apart from the documentation.
Could anyone please explain what is the difference between two and when to use what?
range_iterator is used for get type of range iterator in following way:
range_iterator< SomeRange >::type
It simillar in something to std::iterator_traits. For instance, you may get value type from iterator:
std::iterator_traits<int*>::value_type
iterator_range is bridge between ranges and iterators. For instance - you have pair of iterators, and you want pass them to algorithm which only accepts ranges. In that case you can wrap your iterators into range, using iterator_range. Or better - make_iterator_range - it will help to deduce types (like std::make_pair does):
make_iterator_range(iterator1,iterator2)
returns range.
Consider following example:
live demo
#include <boost/range/iterator_range.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/iterator.hpp>
#include <typeinfo>
#include <iostream>
#include <ostream>
using namespace boost;
using namespace std;
struct print
{
template<typename T>
void operator()(const T &t) const
{
cout << t << " ";
}
};
int main()
{
typedef int Array[20];
cout << typeid( range_iterator<Array>::type ).name() << endl;
Array arr={11,22,33,44,55,66,77,88};
boost::for_each( make_iterator_range(arr,arr+5) ,print());
}
Also, it would be nice if you can point me to sample examples where the boost range library is used to know more about it apart from the documentation
For quick summary - check this slides
Generally, you will not use boost::range_iterator directly, as it is a template metafunction which takes the range given (regardless of the type of the range), and returns the type of it's begin()/end() methods.
boost::iterator_range is used to create a new range from a pair of pre-existing iterators. This you will be more likely to use, usually when taking code that is still iterator based and using that to convert to a range.

What is wrong with `std::set`?

In the other topic I was trying to solve this problem. The problem was to remove duplicate characters from a std::string.
std::string s= "saaangeetha";
Since the order was not important, so I sorted s first, and then used std::unique and finally resized it to get the desired result:
aeghnst
That is correct!
Now I want to do the same, but at the same time I want the order of characters intact. Means, I want this output:
sangeth
So I wrote this:
template<typename T>
struct is_repeated
{
std::set<T> unique;
bool operator()(T c) { return !unique.insert(c).second; }
};
int main() {
std::string s= "saaangeetha";
s.erase(std::remove_if(s.begin(), s.end(), is_repeated<char>()), s.end());
std::cout << s ;
}
Which gives this output:
saangeth
That is, a is repeated, though other repetitions gone. What is wrong with the code?
Anyway I change my code a bit: (see the comment)
template<typename T>
struct is_repeated
{
std::set<T> & unique; //made reference!
is_repeated(std::set<T> &s) : unique(s) {} //added line!
bool operator()(T c) { return !unique.insert(c).second; }
};
int main() {
std::string s= "saaangeetha";
std::set<char> set; //added line!
s.erase(std::remove_if(s.begin(),s.end(),is_repeated<char>(set)),s.end());
std::cout << s ;
}
Output:
sangeth
Problem gone!
So what is wrong with the first solution?
Also, if I don't make the member variable unique reference type, then the problem doesn't go.
What is wrong with std::set or is_repeated functor? Where exactly is the problem?
I also note that if the is_repeated functor is copied somewhere, then every member of it is also copied. I don't see the problem here!
Functors are supposed to be designed in a way where a copy of a functor is identical to the original functor. That is, if you make a copy of one functor and then perform a sequence of operations, the result should be the same no matter which functor you use, or even if you interleave the two functors. This gives the STL implementation the flexibility to copy functors and pass them around as it sees fit.
With your first functor, this claim does not hold because if I copy your functor and then call it, the changes you make to its stored set do not reflect in the original functor, so the copy and the original will perform differently. Similarly, if you take your second functor and make it not store its set by reference, the two copies of the functor will not behave identically.
The reason that your final version of the functor works, though, is because the fact that the set is stored by reference means that any number of copies of tue functor will behave identically to one another.
Hope this helps!
In GCC (libstdc++), remove_if is implemented essentially as
template<typename It, typename Pred>
It remove_if(It first, It last, Pred predicate) {
first = std::find_if(first, last, predicate);
// ^^^^^^^^^
if (first == last)
return first;
else {
It result = first;
++ result;
for (; first != last; ++ first) {
if (!predicate(*first)) {
// ^^^^^^^^^
*result = std::move(*first);
++ result;
}
}
}
}
Note that your predicate is passed by-value to find_if, so the struct, and therefore the set, modified inside find_if will not be propagated back to caller.
Since the first duplicate appears at:
saaangeetha
// ^
The initial "sa" will be kept after the find_if call. Meanwhile, the predicate's set is empty (the insertions within find_if are local). Therefore the loop afterwards will keep the 3rd a.
sa | angeth
// ^^ ^^^^^^
// || kept by the loop in remove_if
// ||
// kept by find_if
Not really an answer, but as another interesting tidbit to consider, this does work, even though it uses the original functor:
#include <set>
#include <iostream>
#include <string>
#include <algorithm>
#include <iterator>
template<typename T>
struct is_repeated {
std::set<T> unique;
bool operator()(T c) { return !unique.insert(c).second; }
};
int main() {
std::string s= "saaangeetha";
std::remove_copy_if(s.begin(), s.end(),
std::ostream_iterator<char>(std::cout),
is_repeated<char>());
return 0;
}
Edit: I don't think it affects this behavior, but I've also corrected a minor slip in your functor (operator() should apparently take a parameter of type T, not char).
I suppose the problem could lie in that the is_repeated functor is copied somewhere inside the implementation of std::remove_if. If that is the case, the default copy constructor is used and this in turn calls std::set copy constructor. You end up with two is_repeated functors possibly used independently. However as the sets in both of them are distinct objects, they don't see the mutual changes. If you turn the field is_repeated::unique to a reference, then the copied functor still uses the original set which is what you want in this case.
Functor classes should be pure functions and have no state of their own. See item 39 in Scott Meyer's Effective STL book for a good explanation on this. But the gist of it is that your functor class may be copied 1 or more times inside the algorithm.
The other answers are correct, in that the issue is that the functor that you are using is not copyable safe. In particular, the STL that comes with gcc (4.2) implements std::remove_if as a combination of std::find_if to locate the first element to delete followed by a std::remove_copy_if to complete the operation.
template <typename ForwardIterator, typename Predicate>
std::remove_if( ForwardIterator first, ForwardIterator end, Predicate pred ) {
first = std::find_if( first, end, pred ); // [1]
ForwardIterator i = it;
return first == last? first
: std::remove_copy_if( ++i, end, fist, pred ); // [2]
}
The copy in [1] means that the first element found is added to the copy of the functor and that means that the first 'a' will be lost in oblivion. The functor is also copied in [2], and that would be fine if it were not because the original for that copy is an empty functor.
Depending on the implementation of remove_if can make copies of your predicate. Either refactor your functor and make it stateless or use Boost.Ref to "for passing references to function templates (algorithms) that would usually take copies of their arguments", like so:
#include <set>
#include <iostream>
#include <string>
#include <algorithm>
#include <iterator>
#include <boost/ref.hpp>
#include <boost/bind.hpp>
template<typename T>
struct is_repeated {
std::set<T> unique;
bool operator()(T c) { return !unique.insert(c).second; }
};
int main() {
std::string s= "saaangeetha";
s.erase(std::remove_if(s.begin(), s.end(), boost::bind<bool>(boost::ref(is_repeated<char>()),_1)), s.end());
std::cout << s;
return 0;
}