I have applied the accepted answer to this question in a templated class, like this:
template <typename T, typename S>
struct sizeCompare {
bool operator() (const std::set<Node<T, S>>& lhs, const std::set<Node<T, S>>& rhs) const {
return lhs.size() < rhs.size() || (lhs.size() == rhs.size() && lhs < rhs);
}
};
template <typename T, typename S>
class Graph {
private:
std::set<std::set<Node <T, S>>, sizeCompare <T, S>> cliques = {};
};
Now I am trying to use std::lower_bound in one of the methods of this class, and for that I want to re-use my sizeCompare structure. I have therefore followed the syntax found in this answer, like this:
// Still in Graph.h
std::set<Node <T, S>> getCliqueOfSize(const lli n) {
// ...
typename std::set<std::set<Node <T, S>>, sizeCompare <T, S>>::const_iterator it = std::lower_bound(cliques.begin(), cliques.end(), n, sizeCompare<T, S>());
// ...
}
However, compiling this yields the following error:
C:/PROGRA~2/CODEBL~1/mingw64/lib/gcc/x86_64-w64-mingw32/6.2.0/include/c++/bits/predefined_ops.h:144:11:
error: no match for call to
(sizeCompare<long long, void*>) (const std::set<Node<long long, void*>, std::less<Node<long long, void*> >, std::allocator<Node<long long, void*> > >&, const long long&)
{ return bool(_M_comp(*__it, __val)); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~
The error stack indicates that this error happens when generating the method shown above.
The only time where I instantiate the Graph class, I use <long long, void *>, so that is where the types shown in the compilation error come from. Can anyone explain why I am getting this error, and possibly how to fix it?
You have a comparator for two sets, but you are calling std::lower_bound() on a set with a value of key_type. That is, you are searching for a key in a set. But your comparator does not support set < long long int, only set < set.
Maybe you need to define an additional comparator:
bool operator() (const std::set<Node<T, S>>& lhs, const T& rhs) const;
Related
I've got the following situation in a class template case:
template<class T1,class T2>
class targetClass{
public:
typedef typename std::pair<T1, T2> ToSortType;
typedef typename std::set<ToSortType> ContainerSort;
void bar(ToSortType a, ToSortType b);
private:
ContainerSort container;
bool operator<(const ToSortType& rhs) const;
}
template<class T1,class T2>
void targetClass<T1,T2>::bar(ToSortType a, ToSortType b){
container.insert(a);
container.insert(b);
}
template <class T1,class T2>
bool targetClass<T1,T2>::operator<(const ToSortType& rhs) const
{
return this->first < rhs.first;
}
In main function something like this:
targetClass<int,T2> anObjectTarget;
T2 a;
T2 b;
anObjectTarget.bar(std::make_pair(0,a),std::make_pair(1,b));
Where T2 is a user-defined type which generally does not have a defined operator<
In this particular case, std::set has to compare std::pair<int,T2> by firstly check an operator< (and others, maybe) for int type and then for T2. In this case, the compiler can not find a suitable operator for T2. In the previous snippet then I make a redefinition of the operator concerned but the compiler complains in this way:
/usr/include/c++/7/bits/stl_pair.h:456: error: no match for ‘operator<’ (operand types are ‘const T2’ and ‘const T2’)
|| (!(__y.first < __x.first) && __x.second < __y.second); }
~~~~~~~~~~~^~~~~~~~~~~~
I've never redefined an operator before but looking to the documentation it looks correct to me (but not to the compiler).
The operator you overloaded is a member of targetClass<T1,T2> and takes two ToSortType as parameter. Thats not how an overload of the < operator works. Consider that for instances of class type the follwing two are equivalent:
a < b
a.operator<(b)
ie operators are just syntactic sugar for calling special member functions. The operator you wrote could only be called like
targetClass<T1,T2> t;
T1 a;
T2 b;
t.operator<(a,b);
but what the set tries to call is a < b, ie a.operator(b) and that apparently does not exist (std::pair<T1,T2> can only be comared via < when both T1 and T2 can).
Long story short: You cannot use your operator to compare two instances of ToSortType.
I would not recommend to try to overload the operator< for std::pair<T1,T2>, but rather use a custom type:
template<class T1,class T2>
class targetClass{
public:
struct value_type {
T1 first;
T2 second;
bool operator<(const value_type& other) {
return first < rhs.first;
}
}
using container_type = std::set<value_type>;
void bar(const value_type& a,const value_type& b);
private:
container_type container;
};
If you want to stay with std::pair then you can use the fact that std::set allows you to chose the type of the comparator. However, first I have to explain a bit not to confuse you because the following may appear to be contradicting the above (it does not). The default comparator that set uses is std::less<Key>, that is a type with an operator() that compares two element of type Key, it is something similar (but not exactly) like this:
template <typename Key>
struct less {
bool operator() (const Key& a,const Key& b) const {
return a < b;
}
};
And this is the place where the compiler cannot find a < for your Key type (which is std::pair<T1,T2>). You can use your own comparator:
template <typename T1,typename T2>
struct my_comparator {
bool operator() (const std::pair<T1,T2>& a, const std::pair<T1,T2>& b) const {
return a.first < b.first;
}
};
And then your set is
using container_type = std::set<typename std::pair<T1,T2>,typename my_comparator<T1,T2>>;
Recently I've been tasked with rewriting some of my code from C++14 to C++03 so that I can make use of ompSs. However, I'm not sure if this particular snippet of code can be rewritten without making use of auto.
I have a template class Field< Type > which is a container for Type. The method from the snippet defines the behavior of the summation of two fields, as the sum of every entry in the field point by point.
Type, can be either a double, or a self designed class Vector. The
behaviour of Vector + Vector, Vector + double, and double + Vector, are fully defined in the definition of class Vector.
The code snippet is able to fully define the intended behaviour for the
summation of Fields in it's four possible scenarios:
Field< double > + Field< Vector >,
Field< Vector > + Field< double >,
Field< double > + Field< double >,
Field< Vector > + Field< Vector >
This is accomplished by creating a response Field, where Type is inferred by the result of the summation of the first element of both vectors.
How can I accomplish the same thing without making use of the awesome auto
operator?
template< typename Type_1, typename Type_2 > const auto operator+ (
const Field< Type_1 >& lhs, const Field< Type_2 >& rhs ) {
auto response = Field <
typename std::remove_const<decltype( lhs.get(0) + rhs.get(0) )>::type
>( lhs.size() );
for( size_t i = 0; i < lhs.size(); i++ ){
response[i] = lhs.get(i) + rhs.get(i);
}
return response;
}
I'm pretty sure that you are out of luck here. The problem is mainly that you can't select a particular overload of operator+ without also specifying the return type, so you can't retrieve it independently in C++03.
What you can do, however, is to write a meta-function that gives the correct return type. If you use this function also to define the operator+ overloads you'll get the same compile-time guarantees and nice-enough errors:
template <typename Lhs, typename Rhs>
struct AddResult;
template <>
struct AddResult<Vector, Vector> { typedef Vector result_type; }
// ...
typename AddResult<Vector, Vector>::result_type operator+(Vector lhs, Vector rhs)
// ...
template< typename Type_1, typename Type_2 >
Field<typename AddResult<Type_1, Type_2>::result_type>
operator+ (const Field<Type_1>& lhs, const Field<Type_2>& rhs)
{
Field<typename AddResult<Type_1, Type_2>::result_type> response(lhs.size());
for (size_t i = 0; i != lhs.size(); i++) {
response[i] = lhs.get(i) + rhs.get(i);
}
return response;
}
Very far from being pretty, but I can't think of a nicer solution.
You can always replace auto with an explicit type.
In this case, you can add a template parameter (to the front of the template so that Type1 and Type2 can still be deduced), and specify the result type at the call site.
template<typename Result, typename Type_1, typename Type_2 >
const Result operator+ ( const Field< Type_1 >& lhs, const Field< Type_2 >& rhs ) ...
I am trying to use the std::tie to implement the operator< in order to create a map of structs that contain a set. The same code without templates seem that it works. I am getting this message code from my compiler:
/usr/include/c++/4.8/bits/stl_algobase.h:888: error: no match for operator< (operand types are const SiPa<int, int> and const SiPa<int, int>)
if (*__first1 < *__first2)
^
Everything compiles if I comment the myMap.insert({akey, true}); line.
Any hints?
template<class I = int, class S = int>
struct SiPa
{
I i;
S s;
};
template<class I = int, class S = int>
struct SiPaComparator
{
bool operator() (const SiPa<I, S>& first, const SiPa<I, S>& second) const
{
return std::tie(first.i, first.s) < std::tie(second.i, second.s);
}
};
template<class I = int, class S = int>
struct AKey
{
typedef std::set< SiPa<I, S>, SiPaComparator<I,S> > SetType;
SetType keySet;
I keyI;
};
template<class I = int, class S = int>
struct AKeyComparator
{
bool operator() (const AKey<I, S>& first, const AKey<I, S>& second) const
{
return std::tie(first.keySet, first.keyI) < std::tie(second.keySet, second.keyI);
}
};
int main()
{
AKey<int,int> akey;
std::map<AKey<int,int>, bool, AKeyComparator<int,int>> myMap;
myMap.insert({akey, true});
}
You need add operator< for struct SiPa, std::map require it
template<class I = int, class S = int>
struct SiPa {
I i;
S s;
bool operator<(const SiPa<I, S> &ref) {
return i < ref.i && s < ref.s;
}
};
In general, comparators on maps and sets are stateful. When comparing two different sets or maps, there is no obvious way to pick which one to use.
So when comparing different sets and maps via <, you get std::lexographical_compare with no Compare argument, which uses <. (Note this sucks for sets of pointers to objects not from the same array)
struct order_by_tie {
template<class Lhs, class Rhs,
class=std::enable_if_t<
std::is_base_of<order_by_tie, Lhs>::value
&& std::is_base_of<order_by_tie, Rhs>::value
>
>
friend bool operator<(Lhs const& lhs, Rhs const& rhs) {
return as_tie(lhs) < as_tie(rhs);
}
};
order_by_tie is intended to be inherited from. It uses ADL (argument dependent lookup) to enable < on its descendent classes, implemented by calling the free function as_tie on each side then doing a <.
We use it as follows:
template<class I = int, class S = int>
struct SiPa:order_by_tie
{
I i;
S s;
friend auto as_tie( SiPa const& self ) {
return std::tie(self.i, self.s);
}
};
template<class I = int, class S = int>
struct AKey:order_by_tie
{
typedef std::set< SiPa<I, S>, SiPaComparator<I,S> > SetType;
SetType keySet;
I keyI;
friend auto as_tie( AKey const& self ) {
return std::tie(self.keySet, self.keyI);
}
};
then
std::map<AKey<int,int>, bool> myMap;
works.
as_tie uses C++14, because the alternative is annoying. You can add a -> decltype(std::tie( blah, blah )) for C++11 (repeating yourself).
According to http://www.cplusplus.com/reference/set/set/operators/
The other operations also use the operators == and < internally to compare the elements, behaving as if the following equivalent operations were performed:
Notice that none of these operations take into consideration the internal comparison object of neither container.
So the comparaison of std::set<SiPa<I, S>, SiPaComparator<I,S>> is done with
operator < (const SiPa<I, S>&, const SiPa<I, S>&)
and not with
SiPaComparator<I, S>{}
The workaround is to define that operator <.
The Problem
I have a custom type A who has natural ordering (having operator<) and multiple alternative orderings (case-sensitive, case-insensitive, etc.). Now I have a std::pair (or std::tuple) consisting (one or more of) A. Here are some examples of types I want to compare: std::pair<A, int>, std::pair<int, A>, std::tuple<A, int, int>, std::tuple<int, A, int>. How can I compare the std::pair (or std::tuple) using the default element-wise comparison implementation, plugging-in my comparison function for A?
The Code
The code below doesn't compile:
#include <utility> // std::pair
#include <tuple> // std::tuple
#include <iostream> // std::cout, std::endl
struct A
{
A(char v) : value(v) {}
char value;
};
// LOCATION-1 (explained in the text below)
int main()
{
std::cout
<< "Testing std::pair of primitive types: "
<< (std::pair<char, int>('A', 1)
<
std::pair<char, int>('a', 0))
<< std::endl;
std::cout
<< "Testing std::tuple of primitive types: "
<< (std::tuple<char, int, double>('A', 1, 1.0)
<
std::tuple<char, int, double>('a', 0, 0.0))
<< std::endl;
// This doesn't compile:
std::cout
<< "Testing std::pair of custom types: "
<< (std::pair<A, int>('A', 1)
<
std::pair<A, int>('a', 0))
<< std::endl;
return 0;
}
It is because operator< isn't defined for struct A. Adding it to LOCATION-1 above would solve the problem:
bool operator<(A const& lhs, A const& rhs)
{
return lhs.value < rhs.value;
}
Now, we have an alternative ordering for struct A:
bool case_insensitive_less_than(A const& lhs, A const& rhs)
{
char const lhs_value_case_insensitive
= ('a' <= lhs.value && lhs.value <= 'z'
? (lhs.value + 0x20)
: lhs.value);
char const rhs_value_case_insensitive
= ('a' <= rhs.value && rhs.value <= 'z'
? (rhs.value + 0x20)
: rhs.value);
return lhs_value_case_insensitive < rhs_value_case_insensitive;
}
Supposed we want to keep the original operator< for struct A (the case-sensitive one), how can we compare std::pair<A, int> with this alternative ordering?
I know that adding a specialized version of operator< for std::pair<A, int> solves the problem:
bool operator<(std::pair<A, int> const& lhs, std::pair<A, int> const& rhs)
{
return (case_insensitive_less_than(lhs.first, rhs.first)
? true
: case_insensitive_less_than(rhs.first, lhs.first)
? false
: (lhs.second < rhs.second));
}
However, I consider this a sub-optimal solution.
Firstly, for std::pair, it is easy to re-implement the element-wise comparison, but for std::tuple it might be complicated (dealing with variadic templates) and error-prone.
Secondly, I can hardly believe that it is the best-practice way to solve the problem: imagine that we have to define a specialized version of operator< for each of the following classes: std::tuple<A, int, int>, std::tuple<int, A, int>, std::tuple<int, int, A>, std::tuple<A, A, int>, ... (It's not even a practical way!)
Re-using the well written built-in operator< for std::tuple and plugging-in my less-than for struct A would be what I want. Is it possible? Thanks in advance!
The easy way would be to manually write compare( tup, tup, f ) that uses f to lexographically compare the elements in the tuples. But that is boring.
// This type wraps a reference of type X&&
// it then overrides == and < with L and E respectively
template<class X, class L, class E>
struct reorder_ref {
using ref = reorder_ref;
X&& x;
friend bool operator<(ref lhs, ref rhs) {
return L{}((X&&) lhs.x, (X&&) rhs.x);
}
friend bool operator==(ref lhs, ref rhs) {
return E{}((X&&) lhs.x, (X&&) rhs.x);
}
// other comparison ops based off `==` and `<` go here
friend bool operator!=(ref lhs, ref rhs){return !(lhs==rhs);}
friend bool operator>(ref lhs, ref rhs){return rhs<lhs;}
friend bool operator<=(ref lhs, ref rhs){return !(lhs>rhs);}
friend bool operator>=(ref lhs, ref rhs){return !(lhs<rhs);}
reorder_ref(X&& x_) : x((X&&) x_) {}
reorder_ref(reorder_ref const&) = default;
};
the above is a reference that changes how we order.
// a type tag, to pass a type to a function:
template<class X>class tag{using type=X;};
// This type takes a less than and equals stateless functors
// and takes as input a tuple, and builds a tuple of reorder_refs
// basically it uses L and E to compare the elements, but otherwise
// uses std::tuple's lexographic comparison code.
template<class L, class E>
struct reorder_tuple {
// indexes trick:
template<class Tuple, class R, size_t... Is>
R operator()(tag<R>, std::index_sequence<Is...>, Tuple const& in) const {
// use indexes trick to do conversion
return R( std::get<Is>(in)... );
}
// forward to the indexes trick above:
template<class... Ts, class R=std::tuple<reorder_ref<Ts const&, L, E>...>>
R operator()(std::tuple<Ts...> const& in) const {
return (*this)(tag<R>{}, std::index_sequence_for<Ts...>{}, in);
}
// pair filter:
template<class... Ts, class R=std::pair<reorder_ref<Ts const&, L, E>...>>
R operator()(std::pair<Ts...> const& in) const {
return (*this)(tag<R>{}, std::index_sequence_for<Ts...>{}, in);
}
};
the above stateless function object takes some new less and equals operations, and maps any tuple to a tuple of reorder_ref<const T, ...>, which change the ordering to follow L and E respectively.
This next type does what std::less<void> does for std::less<T> sort of -- it takes a type-specific stateless ordering function template object, and makes it a type-generic stateless ordering function object:
// This takes a type-specific ordering stateless function type, and turns
// it into a generic ordering function type
template<template<class...> class order>
struct generic_order {
template<class T>
bool operator()(T const& lhs, T const& rhs) const {
return order<T>{}(lhs, rhs);
}
};
so if we have a template<class T>class Z such that Z<T> is an ordering on Ts, the above gives you a universal ordering on anything.
This next one is a favorite of mine. It takes a type T, and orders it based on a mapping to a type U. This is surprisingly useful:
// Suppose there is a type X for which we have an ordering L
// and we have a map O from Y->X. This builds an ordering on
// (Y lhs, Y rhs) -> L( O(lhs), O(rhs) ). We "order" our type
// "by" the projection of our type into another type. For
// a concrete example, imagine we have an "id" structure with a name
// and age field. We can write a function "return s.age;" to
// map our id type into ints (age). If we order by that map,
// then we order the "id" by age.
template<class O, class L = std::less<>>
struct order_by {
template<class T, class U>
bool operator()(T&& t, U&& u) const {
return L{}( O{}((T&&) t), O{}((U&&) u) );
}
};
Now we glue it all together:
// Here is where we build a special order. Suppose we have a template Z<X> that returns
// a stateless order on type X. This takes that ordering, and builds an ordering on
// tuples based on it, using the above code as glue:
template<template<class...>class Less, template<class...>class Equals=std::equal_to>
using tuple_order = order_by< reorder_tuple< generic_order<Less>, generic_order<Equals> > >;
tuple_order does most of the work for us. All we need is to provide it with an element-wise ordering template stateless function object. tuple_order will then produce a tuple ordering functor based on it.
// Here is a concrete use of the above
// my_less is a sorting functiont that sorts everything else the usual way
// but it sorts Foo's backwards
// Here is a toy type. It wraps an int. By default, it sorts in the usual way
struct Foo {
int value = 0;
// usual sort:
friend bool operator<( Foo lhs, Foo rhs ) {
return lhs.value<rhs.value;
}
friend bool operator==( Foo lhs, Foo rhs ) {
return lhs.value==rhs.value;
}
};
template<class T>
struct my_less : std::less<T> {};
// backwards sort:
template<>
struct my_less<Foo> {
bool operator()(Foo const& lhs, Foo const& rhs) const {
return rhs.value < lhs.value;
}
};
using special_order = tuple_order< my_less >;
and bob is your uncle (live example).
special_order can be passed to a std::map or std::set, and it will order any tuples or pairs encountered with my_less replacing the default ordering of the elements.
Suppose I have a bunch of vectors:
vector<int> v1;
vector<double> v2;
vector<int> v3;
all of the same length. Now, for every index i, I would like to be able to treat (v1[i], v2[i], v3[i]) as a tuple, and maybe pass it around. In fact, I want to have a a vector-of-tuples rather than a tuple-of-vectors, using which I can do the above. (In C terms, I might say an array-of-structs rather than a struct-of-arrays). I do not want to effect any data reordering (think: really long vectors), i.e. the new vector is backed by the individual vectors I pass in. Let's .
Now, I want the class I write (call it ToVBackedVoT for lack of a better name) to support any arbitrary choice of vectors to back it (not just 3, not int, double and int, not every just scalars). I want the vector-of-tuples to be mutable, and for no copies to be made on construction/assignments.
If I understand correctly, variadic templates and the new std::tuple type in C++11 are the means for doing this (assuming I don't want untyped void* arrays and such). However, I only barely know them and have never worked with them. Can you help me sketch out how such a class will look like? Or how, given
template <typename ... Ts>
I can express something like "the list of template arguments being the replacement of each typename in the original template arguments with a vector of elements of this type"?
Note: I think I might also want to later be able to adjoin additional vectors to the backing vectors, making an instance of ToVBackedVoT<int, double, int> into, say, an instance of ToVBackedVoT<int, double, int, unsigned int>. So, bear that in mind when answering. This is not critically important though.
One idea is to keep the storage in the "struct of array" style in form of vectors for good performance if only a subset of the fields are used for a particular task. Then, for each kind of task requiring a different set of fields, you can write a lightweight wrapper around some of those vectors, giving you a nice random access iterator interface similar to what std::vector supports.
Concerning the syntax of variadic templates, this is how a wrapper class (without any iterators yet) could look like:
template<class ...Ts> // Element types
class WrapMultiVector
{
// references to vectors in a TUPLE
std::tuple<std::vector<Ts>&...> m_vectors;
public:
// references to vectors in multiple arguments
WrapMultiVector(std::vector<Ts> & ...vectors)
: m_vectors(vectors...) // construct tuple from multiple args.
{}
};
To construct such a templated class, it's often preferred to have a template type deducting helper function available (similar to those make_{pair|tuple|...} functions in std):
template<class ...Ts> // Element types
WrapMultiVector<Ts...> makeWrapper(std::vector<Ts> & ...vectors) {
return WrapMultiVector<Ts...>(vectors...);
}
You already see different types of "unpacking" the type list.
Adding iterators suitable to your application (you requested in particular random access iterators) is not so easy. A start could be forward only iterators, which you might extend to random access iterators.
The following iterator class is capable of being constructed using a tuple of element iterators, being incremented and being dereferenced to obtain a tuple of element references (important for read-write access).
class iterator {
std::tuple<typename std::vector<Ts>::iterator...> m_elemIterators;
public:
iterator(std::tuple<typename std::vector<Ts>::iterator...> elemIterators)
: m_elemIterators(elemIterators)
{}
bool operator==(const iterator &o) const {
return std::get<0>(m_elemIterators) == std::get<0>(o.m_elemIterators);
}
bool operator!=(const iterator &o) const {
return std::get<0>(m_elemIterators) != std::get<0>(o.m_elemIterators);
}
iterator& operator ++() {
tupleIncrement(m_elemIterators);
return *this;
}
iterator operator ++(int) {
iterator old = *this;
tupleIncrement(m_elemIterators);
return old;
}
std::tuple<Ts&...> operator*() {
return getElements(IndexList());
}
private:
template<size_t ...Is>
std::tuple<Ts&...> getElements(index_list<Is...>) {
return std::tie(*std::get<Is>(m_elemIterators)...);
}
};
For demonstration purposes, two different patterns are in this code which "iterate" over a tuple in order to apply some operation or construct a new tuple with some epxression to be called per element. I used both in order to demonstrate alternatives; you can also use the second method only.
tupleIncrement: You can use a helper function which uses meta programming to index a single entry and advance the index by one, then calling a recursive function, until the index is at the end of the tuple (then there is a special case implementation which is triggered using SFINAE). The function is defined outside of the class and not above; here is its code:
template<std::size_t I = 0, typename ...Ts>
inline typename std::enable_if<I == sizeof...(Ts), void>::type
tupleIncrement(std::tuple<Ts...> &tup)
{ }
template<std::size_t I = 0, typename ...Ts>
inline typename std::enable_if<I < sizeof...(Ts), void>::type
tupleIncrement(std::tuple<Ts...> &tup)
{
++std::get<I>(tup);
tupleIncrement<I + 1, Ts...>(tup);
}
This method can't be used to assign a tuple of references in the case of operator* because such a tuple has to be initialized with references immediately, which is not possible with this method. So we need something else for operator*:
getElements: This version uses an index list (https://stackoverflow.com/a/15036110/592323) which gets expanded too and then you can use std::get with the index list to expand full expressions. The IndexList when calling the function instantiates an appropriate index list which is only required for template type deduction in order to get those Is.... The type can be defined in the wrapper class:
// list of indices
typedef decltype(index_range<0, sizeof...(Ts)>()) IndexList;
More complete code with a little example can be found here: http://ideone.com/O3CPTq
Open problems are:
If the vectors have different sizes, the code fails. Better would be to check all "end" iterators for equality; if one iterator is "at end", we're also "at end"; but this would require some logic more than operator== and operator!= unless it's ok to "fake" it in; meaning that operator!= could return false as soon as any operator is unequal.
The solution is not const-correct, e.g. there is no const_iterator.
Appending, inserting etc. is not possible. The wrapper class could add some insert or and / or push_back function in order to make it work similar to std::vector. If your goal is that it's syntactically compatible to a vector of tuples, reimplement all those relevant functions from std::vector.
Not enough tests ;)
An alternative to all the variadic template juggling is to use the boost::zip_iterator for this purpose. For example (untested):
std::vector<int> ia;
std::vector<double> d;
std::vector<int> ib;
std::for_each(
boost::make_zip_iterator(
boost::make_tuple(ia.begin(), d.begin(), ib.begin())
),
boost::make_zip_iterator(
boost::make_tuple(ia.end(), d.end(), ib.end())
),
handle_each()
);
Where your handler, looks like:
struct handle_each :
public std::unary_function<const boost::tuple<const int&, const double&, const int&>&, void>
{
void operator()(const boost::tuple<const int&, const double&, const int&>& t) const
{
// Now you have a tuple of the three values across the vector...
}
};
As you can see, it's pretty trivial to expand this to support an arbitrary set of vectors..
From asker's clarification on how this would be used (code that takes a tuple), I'm going to propose this instead.
//give the i'th element of each vector
template<typename... Ts>
inline tuple<Ts&...> ith(size_t i, vector<Ts>&... vs){
return std::tie(vs[i]...);
}
There's a proposal to allow parameter packs to be saved as members of classes (N3728). Using that, here's some untested and untestable code.
template<typename... Types>
class View{
private:
vector<Types>&... inner;
public:
typedef tuple<Types&...> reference;
View(vector<Types>&... t): inner(t...) {}
//return smallest size
size_t size() const{
//not sure if ... works with initializer lists
return min({inner.size()...});
}
reference operator[](size_t i){
return std::tie(inner[i]...);
}
};
And iteration:
public:
iterator begin(){
return iterator(inner.begin()...);
}
iterator end(){
return iterator(inner.end()...);
}
//for .begin() and .end(), so that ranged-based for can be used
class iterator{
vector<Types>::iterator... ps;
iterator(vector<Types>::iterator... its):ps(its){}
friend View;
public:
//pre:
iterator operator++(){
//not sure if this is allowed.
++ps...;
//use this if not:
// template<typename...Types> void dummy(Types... args){} //global
// dummy(++ps...);
return *this;
}
iterator& operator--();
//post:
iterator operator++(int);
iterator operator--(int);
//dereference:
reference operator*()const{
return std::tie(*ps...);
}
//random access:
iterator operator+(size_t i) const;
iterator operator-(size_t i) const;
//need to be able to check end
bool operator==(iterator other) const{
return std::make_tuple(ps...) == std::make_tuple(other.ps...);
}
bool operator!=(iterator other) const{
return std::make_tuple(ps...) != std::make_tuple(other.ps...);
}
};
You may use something like:
#if 1 // Not available in C++11, so write our own
// class used to be able to use std::get<Is>(tuple)...
template<int... Is>
struct index_sequence { };
// generator of index_sequence<Is>
template<int N, int... Is>
struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> { };
template<int... Is>
struct make_index_sequence<0, Is...> : index_sequence<Is...> { };
#endif
// The 'converting' class
// Note that it doesn't check that vector size are equal...
template<typename ...Ts>
class ToVBackedVoT
{
public:
explicit ToVBackedVoT(std::vector<Ts>&... vectors) : data(vectors...) {}
std::tuple<const Ts&...> operator [] (unsigned int index) const
{
return at(index, make_index_sequence<sizeof...(Ts)>());
}
std::tuple<Ts&...> operator [] (unsigned int index)
{
return at(index, make_index_sequence<sizeof...(Ts)>());
}
private:
template <int... Is>
std::tuple<const Ts&...> at(unsigned int index, index_sequence<Is...>) const
{
return std::tie(std::get<Is>(data)[index]...);
}
template <int... Is>
std::tuple<Ts&...> at(unsigned int index, index_sequence<Is...>)
{
return std::tie(std::get<Is>(data)[index]...);
}
private:
std::tuple<std::vector<Ts>&...> data;
};
And to iterate, create an 'IndexIterator' like the one in https://stackoverflow.com/a/20272955/2684539
To adjoin additional vectors, you have to create an other ToVBackedVoT as std::tuple_cat does for std::tuple
Conversion to a std::tuple of vectors (vector::iterators):
#include <iostream>
#include <vector>
// identity
// ========
struct identity
{
template <typename T>
struct apply {
typedef T type;
};
};
// concat_operation
// ================
template <typename Operator, typename ...> struct concat_operation;
template <
typename Operator,
typename ...Types,
typename T>
struct concat_operation<Operator, std::tuple<Types...>, T>
{
private:
typedef typename Operator::template apply<T>::type concat_type;
public:
typedef std::tuple<Types..., concat_type> type;
};
template <
typename Operator,
typename ...Types,
typename T,
typename ...U>
struct concat_operation<Operator, std::tuple<Types...>, T, U...>
{
private:
typedef typename Operator::template apply<T>::type concat_type;
public:
typedef typename concat_operation<
Operator,
std::tuple<Types..., concat_type>,
U...>
::type type;
};
template <
typename Operator,
typename T,
typename ...U>
struct concat_operation<Operator, T, U...>
{
private:
typedef typename Operator::template apply<T>::type concat_type;
public:
typedef typename concat_operation<
Operator,
std::tuple<concat_type>,
U...>
::type type;
};
// ToVectors (ToVBackedVoT)
// =========
template <typename ...T>
struct ToVectors
{
private:
struct to_vector {
template <typename V>
struct apply {
typedef typename std::vector<V> type;
};
};
public:
typedef typename concat_operation<to_vector, T...>::type type;
};
// ToIterators
// ===========
template <typename ...T>
struct ToIterators;
template <typename ...T>
struct ToIterators<std::tuple<T...>>
{
private:
struct to_iterator {
template <typename V>
struct apply {
typedef typename V::iterator type;
};
};
public:
typedef typename concat_operation<to_iterator, T...>::type type;
};
int main() {
typedef ToVectors<int, double, float>::type Vectors;
typedef ToVectors<Vectors, int, char, bool>::type MoreVectors;
typedef ToIterators<Vectors>::type Iterators;
// LOG_TYPE(Vectors);
// std::tuple<
// std::vector<int, std::allocator<int> >,
// std::vector<double, std::allocator<double> >,
// std::vector<float, std::allocator<float> > >
// LOG_TYPE(Iterators);
// std::tuple<
// __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >,
// __gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >,
// __gnu_cxx::__normal_iterator<float*, std::vector<float, std::allocator<float> > > >
}
As an alternative similar to boost::zip_iterator I wrote a zip function with a very simple interface:
vector<int> v1;
vector<double> v2;
vector<int> v3;
auto vec_of_tuples = zip(v1, v2, v3);
For example, iterate over these tuples:
for (auto tuple : zip(v1, v2, v3)) {
int x1; double x2; int x3;
std::tie(x1, x2, x3) = tuple;
//...
}
Here, zip() takes any number of ranges of any type. It returns an adaptor which can be seen as a lazily evaluated range over a tuple of elements originating from the wrapped ranges.
The adaptor is part of my Haskell-style functional library "fn" and implemented using variadic templates.
Currently it doesn't support modification of the original ranges' values via the adaptor because of the design of the library (it's intended to be used with non-mutable ranges like in functional programming).
A brief explanation on how this is done is: zip(...) returns an adaptor object which implements begin() and end(), returning an iterator object. The iterator holds a tuple of iterators to the wrapped ranges. Incrementing the iterator increments all wrapped iterators (which is implemented using an index list and unpacking an incrementing expression into a series of expressions: ++std::get<I>(iterators)...). Dereferencing the iterator will decrement all wrapped iterators and pass it to std::make_tuple (which is also implemented as unpacking the expression *std::get<I>(iterators)...).
P.S. Its implementation is based on a lot of ideas coming from answers to this question.