I have constructed a minimal working example to show a problem I've encountered using STL iterators. I'm using istream_iterator to read floatss (or other types) from a std::istream:
#include <iostream>
#include <iterator>
#include <algorithm>
int main() {
float values[4];
std::copy(std::istream_iterator<float>(std::cin), std::istream_iterator<float>(), values);
std::cout << "Read exactly 4 floats" << std::endl; // Not true!
}
This reads all possible floatss, until EOF into values, which is of fixed size, 4, so now clearly I want to limit the range to avoid overflows and read exactly/at most 4 values.
With more "normal" iterators (i.e. RandomAccessIterator), provided begin+4 isn't past the end you'd do:
std::copy(begin, begin+4, out);
To read exactly 4 elements.
How does one do this with std::istream_iterator? The obvious idea is to change the call to std::copy to be:
std::copy(std::istream_iterator<float>(std::cin), std::istream_iterator<float>(std::cin)+4, values);
But (fairly predictably) this doesn't compile, there are no candidates for operator+:
g++ -Wall -Wextra test.cc
test.cc: In function ‘int main()’:
test.cc:7: error: no match for ‘operator+’ in ‘std::istream_iterator<float, char, std::char_traits<char>, long int>(((std::basic_istream<char, std::char_traits<char> >&)(& std::cin))) + 4’
Any suggestions? Is there a correct, "STLified" pre-C++0x way to achieve this? Obviously I could just write it out as a for loop, but I'm looking to learn something about the STL here. I half wondered about abusing std::transform or std::merge etc. to achieve this functionality somehow, but I can't quite see how to do it.
Take a look at std::copy_n
As you requested a non-C++0x solution, here's an alternative that uses std::generate_n and a generator functor rather than std::copy_n and iterators:
#include <algorithm>
#include <string>
#include <istream>
#include <ostream>
#include <iostream>
template<
typename ResultT,
typename CharT = char,
typename CharTraitsT = std::char_traits<CharT>
>
struct input_generator
{
typedef ResultT result_type;
explicit input_generator(std::basic_istream<CharT, CharTraitsT>& input)
: input_(&input)
{ }
ResultT operator ()() const
{
// value-initialize so primitives like float
// have a defined value if extraction fails
ResultT v((ResultT()));
*input_ >> v;
return v;
}
private:
std::basic_istream<CharT, CharTraitsT>* input_;
};
template<typename ResultT, typename CharT, typename CharTraitsT>
inline input_generator<ResultT, CharT, CharTraitsT> make_input_generator(
std::basic_istream<CharT, CharTraitsT>& input
)
{
return input_generator<ResultT, CharT, CharTraitsT>(input);
}
int main()
{
float values[4];
std::generate_n(values, 4, make_input_generator<float>(std::cin));
std::cout << "Read exactly 4 floats" << std::endl;
}
If you wanted to, you could then use this generator in conjunction with boost::generator_iterator to use the generator as an input iterator.
If you don't have std::copy_n available, it's pretty easy to write your own:
namespace std_ext {
template<class InputIterator, class Size, class OutputIterator>
OutputIterator copy_n(InputIterator first, Size n, OutputIterator result) {
for (int i=0; i<n; i++) {
*result = *first;
++result;
++first;
}
return result;
}
}
Related
I would like to generalize a function using templates. And, the template parameter should be checked somehow.
As a minimum reproducable example I have written a function that will return the top frequent element of a container. For better understanding, please see the below simple code:
#include <iostream>
#include <utility>
#include <unordered_map>
#include <queue>
#include <vector>
// Predicate Functor
template <class Container> struct LessForSecondOfPair {
bool operator () (const std::pair<typename Container::value_type, size_t>& p1, const std::pair<typename Container::value_type, size_t>& p2) { return p1.second < p2.second; }
};
// Function to get most frequent used number in a Container
template <class Container>
auto topFrequent(const Container& data) {
using Counter = std::unordered_map<Container::value_type, size_t>;
using MaxHeap = std::priority_queue<std::pair<Container::value_type, size_t>, std::vector<std::pair<Container::value_type, size_t>>, LessForSecondOfPair<Container>>;
// Count all occurences of data
Counter counter{};
for (const auto& d : data) counter[d]++;
// Build a Max-Heap
MaxHeap maxHeap(counter.begin(), counter.end());
// Return most frequent number
return maxHeap.top().first;
}
int main() {
std::vector test{ 1,2,2,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,6 };
std::cout << "\n\nMost frequent used number is: " << topFrequent(test) << "\n\n";
return 0;
}
As you can see, I use Container::value_type. And I use a range based for, which requires also certain properties.
So, I simply write "value_type" and assume that it exists, without knowing.
How can I check that this works?
I searched a little bit and found stacks of templates and specialized templates, but is there no easier way?
Or shall I do simply nothing and let the compilation fail? No idea . . .
I am still on C++17
Since the range-for is required, you need to test if begin() and end() are supported.
Here top_frequent_support re-uses this trick.
Moreover, expecting the value type to be named as Container::value_type (by the way, your code misses typename) might be an unnecessary constraint.
You can simply deduce this type from *begin(), in a using alias declaration (ValueType here) without expecting a specific name for this type.
This makes your example work even with a simple C-like array.
The tests in main() show the same behaviour for a standard container and a C-like array, but a fallback for any unsuitable argument.
/**
g++ -std=c++17 -o prog_cpp prog_cpp.cpp \
-pedantic -Wall -Wextra -Wconversion -Wno-sign-conversion \
-g -O0 -UNDEBUG -fsanitize=address,undefined
**/
#include <iostream>
#include <utility>
#include <unordered_map>
#include <queue>
#include <vector>
using std::begin; // fallback if not found via ADL
using std::end; // fallback if not found via ADL
template<typename Container>
using top_frequent_support =
decltype((begin(std::declval<const Container &>())),
(end(std::declval<const Container &>())),
void(), 1);
// Predicate Functor
template<typename Container,
top_frequent_support<Container> =1>
struct LessForSecondOfPair2 {
using ValueType = std::decay_t<decltype(*begin(
std::declval<const Container &>()))>;
bool operator () (const std::pair<ValueType, size_t>& p1,
const std::pair<ValueType, size_t>& p2)
{ return p1.second < p2.second; }
};
template<typename Container,
top_frequent_support<Container> =1>
auto topFrequent2(const Container &data) {
using ValueType = std::decay_t<decltype(*begin(data))>;
using Counter = std::unordered_map<ValueType, size_t>;
using MaxHeap = std::priority_queue<std::pair<ValueType, size_t>,
std::vector<std::pair<ValueType, size_t>>,
LessForSecondOfPair2<Container>>;
// Count all occurences of data
Counter counter{};
for (const auto& d : data) counter[d]++;
// Build a Max-Heap
MaxHeap maxHeap(counter.begin(), counter.end());
// Return most frequent number
return maxHeap.top().first;
}
auto topFrequent2(...) {
return "container is not suitable";
}
int main() {
std::vector test{ 1,2,2,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,6 };
std::cout << "Most frequent used number is: " << topFrequent2(test) << "\n";
double arr[]={1.1, 2.2, 2.2, 3.3, 3.3, 3.3};
std::cout << "Most frequent used number is: " << topFrequent2(arr) << "\n";
double value=12.34;
std::cout << "Most frequent used number is: " << topFrequent2(value) << "\n";
return 0;
}
What's the use of value_type in STL containers?
From the MSDN:
// vector_value_type.cpp
// compile with: /EHsc
#include <vector>
#include <iostream>
int main( )
{
using namespace std;
vector<int>::value_type AnInt;
AnInt = 44;
cout << AnInt << endl;
}
I don't understand what does value_type achieve here?
The variable could be an int as well? Is it used because the coders are lazy to check what's the type of objects present in the vector?
I think these are also similar to it allocator_type,size_type,difference_type,reference,key_type etc..
Yes, in your example, it is pretty easy to know that you need an int. Where it gets complicated is generic programming. For example, if I wanted to write a generic sum() function, I would need it to know what kind of container to iterate and what type its elements are, so I would need to have something like this:
template<typename Container>
typename Container::value_type sum(const Container& cont)
{
typename Container::value_type total = 0;
for (const auto& e : cont)
total += e;
return total;
}
The following codes come from an example code to illustrate how to use boost::type_traits. It will use two methods to swap two variables. It is easy to understand that when the two variables are integer (int) their type traits correspond to true_type. However, when two variables are bool type they are not regarded as true_type any more. Why would it happen? Thanks.
#include <iostream>
#include <typeinfo>
#include <algorithm>
#include <iterator>
#include <vector>
#include <memory>
#include <boost/test/included/prg_exec_monitor.hpp>
#include <boost/type_traits.hpp>
using std::cout;
using std::endl;
using std::cin;
namespace opt{
//
// iter_swap:
// tests whether iterator is a proxying iterator or not, and
// uses optimal form accordingly:
//
namespace detail{
template <typename I>
static void do_swap(I one, I two, const boost::false_type&)
{
typedef typename std::iterator_traits<I>::value_type v_t;
v_t v = *one;
*one = *two;
*two = v;
}
template <typename I>
static void do_swap(I one, I two, const boost::true_type&)
{
using std::swap;
swap(*one, *two);
}
}
template <typename I1, typename I2>
inline void iter_swap(I1 one, I2 two)
{
//
// See is both arguments are non-proxying iterators,
// and if both iterator the same type:
//
typedef typename std::iterator_traits<I1>::reference r1_t;
typedef typename std::iterator_traits<I2>::reference r2_t;
typedef boost::integral_constant<bool,
::boost::is_reference<r1_t>::value
&& ::boost::is_reference<r2_t>::value
&& ::boost::is_same<r1_t, r2_t>::value> truth_type;
detail::do_swap(one, two, truth_type());
}
}; // namespace opt
int cpp_main(int argc, char* argv[])
{
//
// testing iter_swap
// really just a check that it does in fact compile...
std::vector<int> v1;
v1.push_back(0);
v1.push_back(1);
std::vector<bool> v2;
v2.push_back(0);
v2.push_back(1);
opt::iter_swap(v1.begin(), v1.begin()+1);
opt::iter_swap(v2.begin(), v2.begin()+1);
return 0;
}
You've got the answer there in your code (as a comment):
See is both arguments are non-proxying iterators
vector<bool> has proxy iterators, because you can't refer directly to a bit.
If vector<bool> stored its' elements as individual booleans (taking 1-4 bytes/entry, depending on the system), the iterators could be non-proxying. But instead, vector<bool> stores 8 entries/byte and uses proxy iterators.
I have a piece of code to use the RTTI as case selector
#include <iostream>
#include <vector>
#include <typeinfo>
#include <complex>
#include <algorithm>
using namespace std;
typedef complex<int> data;
typedef std::vector< data > List;
template <typename T> void zeros(T &v)
{
if (typeid(typename T::value_type)==typeid(complex<int>))
std::fill(v.begin(), v.end(), complex<int>(0, 0));
else std::fill(v.begin(), v.end(), static_cast<typename T::value_type>(0));
}
int main(int argc, char *argv[])
{
List A;
zeros(A);
return 0;
}
In above code, I use typename T::value_type to determine the data type of the list, if there is complex then I will fill the list with complex zero. Otherwise, make it zero of the corresponding type. In above code, if I set data to be int, it does compile and run without any problem. But if I set it another other type, it fail to compile. I wonder if there is any way to make above code work for different type of data (for float, double, long, int, complex, complex and complex).
p.s. the reason I want to fill the vector in two cases because I am going to create my own complex for some reason and I have to tell the code how to create the complex zero or real zero. But for testing, I can't even make it work with built-in datatype
It seems that you want both a default initialized complex (0, 0), and a zero initialized int. You can achieve this using value initialization:
template <typename T> void zeros(T &v)
{
std::fill(v.begin(), v.end(), typename T::value_type{});
}
If you really want to perform some branching depending on what is T::value_type, then it would be better to use some type trait, like the following one:
template<typename T>
struct default_value {
static const T value;
};
template<>
struct default_value<std::complex<int>> { // assuming you'll use other complex type
static const std::complex<int> value;
};
template<typename T>
const T default_value<T>::value = T{};
const std::complex<int> default_value<std::complex<int>>::value = std::complex<int>(0, 0);
// now use it
template <typename T> void zeros(T &v)
{
std::fill(v.begin(), v.end(), default_value<typename T::value_type>::value);
}
But again, default construction and value initialization for built in types should be enough.
Is there a nice implementation of the algorithm to calculate the convolution of two ranges in C++ STL (or even boost)?
i.e. something with prototype (convolution of two ranges a..b and c..d):
template< class Iterator >
void convolution(Iterator a, Iterator b, Iterator c, Iterator d);
which modifies a..b range
Yes std::transform
std::transform(a, b, c, a, Op);
// a b is the the first input range
// c is the start of the second range (which must be at least as large as (b-a)
//
// We then use a as the output iterator as well.
// Op is a BinaryFunction
To answer the comment on how to perform accumulation of state in the comments:
struct Operator
{
State& state;
Operator(Sate& state) : state(state) {}
Type operator()(TypeR1 const& r1Value, TypeR2 const& r2Value) const
{
Plop(state, r1Value, r2Value);
return Convolute(state, r2Value, r2Value);
}
};
State theState = 0;
Operator Op(theState);
I'm not quite sure what a "convolution" from two sequences to one of these two sequences is supposed to be: It seems to be a different understanding than my understanding. Below is a version of convolution using a variable number of iterators. Because I'm actually just too lazy for now, I'll use a somewhat uncommon notion of passing the destination iterator as first argument rather than as last argument. Here is an implementation of a corresponding zip() algorithms:
#include <tuple>
namespace algo
{
template <typename... T>
void dummy(T...)
{
}
template <typename To, typename InIt, typename... It>
To zip(To to, InIt it, InIt end, It... its)
{
for (; it != end; ++it, ++to) {
*to = std::make_tuple(*it, *its...);
algo::dummy(++its...);
}
return to;
}
}
Below is a simple test program I used to verify that the above does what I intended it to do:
#include <deque>
#include <iostream>
#include <iterator>
#include <list>
#include <vector>
enum class e { a = 'a', b = 'b', c = 'c' };
std::ostream& operator<< (std::ostream& out,
std::tuple<int, double, e> const& v)
{
return out << "["
<< std::get<0>(v) << ", "
<< std::get<1>(v) << ", "
<< char(std::get<2>(v)) << "]";
}
int main()
{
typedef std::tuple<int, double, e> tuple;
std::vector<int> v{ 1, 2, 3 };
std::deque<double> d{ 1.1, 2.2, 3.3 };
std::list<e> l{ e::a, e::b, e::c };
std::vector<tuple> r;
algo::zip(std::back_inserter(r), v.begin(), v.end(), d.begin(), l.begin());
std::copy(r.begin(), r.end(),
std::ostream_iterator<tuple>(std::cout, "\n"));
}