operator overloading and template specialization - c++

I have a template class template<typename T, typename R>. R is of type vector<T*> or list<T*>.
I want my class to overload [] operator so that in case it is a vector I will use the built in [] operator for efficiency and in case it's a list I will implement it with iterator.
To me it sounds like a job for template specialization so I thought to write something like this:
template<typename T, typename R>
T& tContainer_t<T, R>::operator[]( unsigned i )
{
//TODO with iterators
}
template<>
T& tContainer_t::operator[]<T, std::vector<T*> >( unsigned i )
{
// TODO with built in [] operator
}
This is wrong and the compiler doesn't allow this.
Is there a way to make it work, or should I use typeid() to differ the two objects at runtime and act accordingly ?

The way to do it with templates is to make a static helper function in a class that can be partially specialized. However, what I would do is:
template<typename T, typename R>
T& tContainer_t<T, R>::operator[]( unsigned i )
{
//assuming that the container refernce is name container;
typename R::iterator itr = container.begin();
std::advance(itr, i);
return *itr;
}
std::advance is guaranteed that for a container with random access iterators (such as vector), it is constant time (basically, it does iterator + n), it can be as fast as doing the pointer lookup vector performs. Otherwise, it does iterator++ n times, which will be linear time. The const version will use const_iterator, but is essentially the same.
Doing it this way will let you properly handle different types of containers (not just vector and list), without having to modify the code.

You don't have to overload the operator. The library aleady contains overloaded functions to help you. std::advance will move an iterator, taking advantage of operator+() for random access iterators.
template<typename T, typename R>
T& tContainer_t<T, R>::operator[]( unsigned i )
{
typename R::iterator it = myContainer.begin();
std::advance(it, i);
return *it;
}

Related

Can type arguments be made deduceable for function templates using std container?

I found this implementation of a few common features of functional programming, e.g. map / reduce:
(I'm aware stuff like that is aparently coming or partially present in new C++ versions)
github link
A part of the code:
template <typename T, typename U>
U foldLeft(const std::vector<T>& data,
const U& initialValue,
const std::function<U(U,T)>& foldFn) {
typedef typename std::vector<T>::const_iterator Iterator;
U accumulator = initialValue;
Iterator end = data.cend();
for (Iterator it = data.cbegin(); it != end; ++it) {
accumulator = foldFn(accumulator, *it);
}
return accumulator;
}
template <typename T, typename U>
std::vector<U> map(const std::vector<T>& data, const std::function<U(T)> mapper) {
std::vector<U> result;
foldLeft<T, std::vector<U>&>(data, result, [mapper] (std::vector<U>& res, T value) -> std::vector<U>& {
res.push_back(mapper(value));
return res;
});
return result;
}
Usage example:
std::vector<int> biggerInts = map<int,int>(test, [] (int num) { return num + 10; });
The type arguments T,U have to be fully qualified for this to compile, as shown in the example, with e.g. map< int,int >( ... ).
This implementation is for C++11, as mentioned on the linked-to page.
Is it possible with newer C++ versions (or even 11) now to make the use of this less verbose, i.e. making the types U,T deduce automatically?
I have googled for that and only found that there is apparently some improvement for class template, as opposed to function template, argument deduction in C++17.
But since I only ever used templates in a rather basic manner, I was wondering whether there is something in existence that I'm not aware of which could improve this implementation verboseness-wise.
You can rewrite map signature to be:
template <typename T, typename M, typename U = decltype(std::declval<M>()(T{}))>
std::vector<U> map(const std::vector<T>& data, const M mapper)
then T will be deduced as value_type of vector's items.
M is any callable object.
U is deduced as return type of M() functor when called for T{}.
Below
std::vector<int> biggerInts = map(test, [] (int num) { return num + 10; });
^^^^ empty template arguments list
works fine.
Live demo
More general templates make template argument deduction easier.
One principle: it is often a mistake to use a std::function as a templated function's parameter. std::function is a type erasure, for use when something needs to store some unknown invokable thing as a specific type. But templates already have the ability to handle any arbitrary invokable type. So if we just use a generic typename FuncT template parameter, it can be deduced for a raw pointer-to-function, a lambda, or another class with operator() directly.
We might as well also get more general and accept any input container instead of just vector, then determine T from it, if it's even directly needed.
So for C++11 I would rewrite these:
// C++20 is adding std::remove_cvref, but it's trivial to implement:
template <typename T>
using remove_cvref_t =
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
template <typename Container, typename U, typename FuncT>
remove_cvref_t<U> foldLeft(
const Container& data,
U&& initialValue,
const FuncT& foldFn) {
remove_cvref_t<U> accumulator = std::forward<U>(initialValue);
for (const auto& elem : data) {
accumulator = foldFn(std::move(accumulator), elem);
}
return accumulator;
}
template <typename Container, typename FuncT>
auto map(const Container& data, const FuncT& mapper)
-> std::vector<remove_cvref_t<decltype(mapper(*std::begin(data)))>>
{
using T = remove_cvref_t<decltype(*std::begin(data))>;
using ResultT = std::vector<remove_cvref_t<decltype(mapper(std::declval<const T&>()))>>;
ResultT result;
foldLeft(data, std::ref(result), [&mapper] (ResultT &res, const T& value) -> ResultT& {
res.push_back(mapper(value));
return res;
});
return result;
}
See the working program on coliru.
There was one unfortunate thing about the old map: it potentially copied the result vector at every iteration. The = in accumulator = foldFn(accumulator, *it); is a self-assignment, which might do nothing, or might allocate new memory, copy contents, then free the old memory and update the container. So instead I've changed the U for foldLeft in this case to a std::reference_wrapper. The = in that case will still "rebind" the wrapper to the same object, but that will at least be quick.
In C++14 and later, you could do away with finding T within map by using a generic lambda: [&mapper] (std::vector<U>& res, const auto& value) ...

What is the C++ equivalent of Python's "in" operator?

What is the C++ way of checking if an element is contained in an array/list, similar to what the in operator does in Python?
if x in arr:
print "found"
else
print "not found"
How does the time complexity of the C++ equivalent compare to Python's in operator?
The time complexity of Python's in operator varies depending on the data structure it is actually called with. When you use it with a list, complexity is linear (as one would expect from an unsorted array without an index). When you use it to look up set membership or presence of a dictionary key complexity is constant on average (as one would expect from a hash table based implementation):
https://wiki.python.org/moin/TimeComplexity
In C++ you can use std::find to determine whether or not an item is contained in a std::vector. Complexity is said to be linear (as one would expect from an unsorted array without an index). If you make sure the vector is sorted, you can also use std::binary_search to achieve the same in logarithmic time.
http://en.cppreference.com/w/cpp/algorithm/find
Check if element is in the list (contains)
Check if element found in array c++
http://en.cppreference.com/w/cpp/algorithm/binary_search
The associative containers provided by the standard library (std::set, std::unordered_set, std::map, ...) provide the member functions find() and count() and contains() (C++20) for this. These will perform better than linear search, i.e., logarithmic or constant time depending on whether you have picked the ordered or the unordered alternative. Which one of these functions to prefer largely depends on what you want to achieve with that info afterwards, but also a bit on personal preference. (Lookup the documentation for details and examples.)
How to check that an element is in a std::set?
How to check if std::map contains a key without doing insert?
https://en.wikipedia.org/wiki/Associative_containers
http://en.cppreference.com/w/cpp/container
If you want to, you can use some template magic to write a wrapper function that picks the correct method for the container at hand, e.g., as presented in this answer.
You can approach this in two ways:
You can use std::find from <algorithm>:
auto it = std::find(container.begin(), container.end(), value);
if (it != container.end())
return it;
or you can iterate through every element in your containers with for ranged loops:
for(const auto& it : container)
{
if(it == value)
return it;
}
Python does different things for in depending on what kind of container it is. In C++, you'd want the same mechanism. Rule of thumb for the standard containers is that if they provide a find(), it's going to be a better algorithm than std::find() (e.g. find() for std::unordered_map is O(1), but std::find() is always O(N)).
So we can write something to do that check ourselves. The most concise would be to take advantage of C++17's if constexpr and use something like Yakk's can_apply:
template <class C, class K>
using find_t = decltype(std::declval<C const&>().find(std::declval<K const&>()));
template <class Container, class Key>
bool in(Container const& c, Key const& key) {
if constexpr (can_apply<find_t, Container, Key>{}) {
// the specialized case
return c.find(key) != c.end();
} else {
// the general case
using std::begin; using std::end;
return std::find(begin(c), end(c), key) != end(c);
}
}
In C++11, we can take advantage of expression SFINAE:
namespace details {
// the specialized case
template <class C, class K>
auto in_impl(C const& c, K const& key, int )
-> decltype(c.find(key), true) {
return c.find(key) != c.end();
}
// the general case
template <class C, class K>
bool in_impl(C const& c, K const& key, ...) {
using std::begin; using std::end;
return std::find(begin(c), end(c), key) != end(c);
}
}
template <class Container, class Key>
bool in(Container const& c, Key const& key) {
return details::in_impl(c, key, 0);
}
Note that in both cases we have the using std::begin; using std::end; two-step in order to handle all the standard containers, raw arrays, and any use-provided/adapted containers.
This gives you an infix *in* operator:
namespace notstd {
namespace ca_helper {
template<template<class...>class, class, class...>
struct can_apply:std::false_type{};
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<template<class...>class Z, class...Ts>
struct can_apply<Z,void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = ca_helper::can_apply<Z,void,Ts...>;
namespace find_helper {
template<class C, class T>
using dot_find_r = decltype(std::declval<C>().find(std::declval<T>()));
template<class C, class T>
using can_dot_find = can_apply< dot_find_r, C, T >;
template<class C, class T>
constexpr std::enable_if_t<can_dot_find<C&, T>{},bool>
find( C&& c, T&& t ) {
using std::end;
return c.find(std::forward<T>(t)) != end(c);
}
template<class C, class T>
constexpr std::enable_if_t<!can_dot_find<C&, T>{},bool>
find( C&& c, T&& t ) {
using std::begin; using std::end;
return std::find(begin(c), end(c), std::forward<T>(t)) != end(c);
}
template<class C, class T>
constexpr bool finder( C&& c, T&& t ) {
return find( std::forward<C>(c), std::forward<T>(t) );
}
}
template<class C, class T>
constexpr bool find( C&& c, T&& t ) {
return find_helper::finder( std::forward<C>(c), std::forward<T>(t) );
}
struct finder_t {
template<class C, class T>
constexpr bool operator()(C&& c, T&& t)const {
return find( std::forward<C>(c), std::forward<T>(t) );
}
constexpr finder_t() {}
};
constexpr finder_t finder{};
namespace named_operator {
template<class D>struct make_operator{make_operator(){}};
template<class T, char, class O> struct half_apply { T&& lhs; };
template<class Lhs, class Op>
half_apply<Lhs, '*', Op> operator*( Lhs&& lhs, make_operator<Op> ) {
return {std::forward<Lhs>(lhs)};
}
template<class Lhs, class Op, class Rhs>
auto operator*( half_apply<Lhs, '*', Op>&& lhs, Rhs&& rhs )
-> decltype( named_invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) ) )
{
return named_invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
}
}
namespace in_helper {
struct in_t:notstd::named_operator::make_operator<in_t> {};
template<class T, class C>
bool named_invoke( T&& t, in_t, C&& c ) {
return ::notstd::find(std::forward<C>(c), std::forward<T>(t));
}
}
in_helper::in_t in;
}
On a flat container, like a vector array or string, it is O(n).
On an associative sorted container, like a std::map, std::set, it is O(lg(n)).
On an unordered associated container, like std::unordered_set, it is O(1).
Test code:
std::vector<int> v{1,2,3};
if (1 *in* v)
std::cout << "yes\n";
if (7 *in* v)
std::cout << "no\n";
std::map<std::string, std::string, std::less<>> m{
{"hello", "world"}
};
if ("hello" *in* m)
std::cout << "hello world\n";
Live example.
C++14, but mainly for enable_if_t.
So what is going on here?
Well, can_apply is a bit of code that lets me write can_dot_find, which detects (at compile time) if container.find(x) is a valid expression.
This lets me dispatch the searching code to use member-find if it exists. If it doesn't exist, a linear search using std::find is used instead.
Which is a bit of a lie. If you define a free function find(c, t) in the namespace of your container, it will use that rather than either of the above. But that is me being fancy (and it lets you extend 3rd party containers with *in* support).
That ADL (argument dependent lookup) extensibity (the 3rd party extension ability) is why we have three different functions named find, two in a helper namespace and one in notstd. You are intended to call notstd::find.
Next, we want a python-like in, and what is more python like than an infix operator? To do this in C++ you need to wrap your operator name in other operators. I chose *, so we get an infix *in* named operator.
TL;DR
You do using notstd::in; to import the named operator in.
After that, t *in* c first checks if find(t,c) is valid. If not, it checks if c.find(t) is valid. If that fails, it does a linear search of c using std::begin std::end and std::find.
This gives you very good performance on a wide variety of std containers.
The only thing it doesn't support is
if (7 *in* {1,2,3})
as operators (other than =) cannot deduce initializer lists I believe. You could get
if (7 *in* il(1,2,3))
to work.
I guess one might make use of this thread and create a custom version of in function.
The main idea is to use SFINAE (Substitution Failure Is Not An Error) to differentiate associative containers (which have key_type member) from sequence containers (which have no key_type member).
Here is a possible implementation:
namespace detail
{
template<typename, typename = void>
struct is_associative : std::false_type {};
template<typename T>
struct is_associative<T,
std::enable_if_t<sizeof(typename T::key_type) != 0>> : std::true_type {};
template<typename C, typename T>
auto in(const C& container, const T& value) ->
std::enable_if_t<is_associative<C>::value, bool>
{
using std::cend;
return container.find(value) != cend(container);
}
template<typename C, typename T>
auto in(const C& container, const T& value) ->
std::enable_if_t<!is_associative<C>::value, bool>
{
using std::cbegin;
using std::cend;
return std::find(cbegin(container), cend(container), value) != cend(container);
}
}
template<typename C, typename T>
auto in(const C& container, const T& value)
{
return detail::in(container, value);
}
Small usage example on WANDBOX.
You can use std::find from <algorithm>, but this works only for datatypes like: std::map and std::vector (etc).
Also note that this will return, iterator to the first element that is found equal to the value you pass, unlike the in operator in Python that returns a bool.
I think one of the nice features of the "in" operator in python is that it can be used with different data types (strings v/s strings, numbers v/s lists, etc).
I am developing a library for using python constructions in C++. It includes "in" and "not_in" operators.
It is based on the same technique used to implement the in operator posted in a previous answer, in which make_operator<in_t> is implemented. However, it is extended for handling more cases:
Searching a string inside a string
Searching an element inside vector and maps
It works by defining several overloads for a function: bool in__(T1 &v1, T2 &v2), in which T1 and T2 consider different possible types of objects. Also, overloads for a function: bool not_in__(T1 &v1, T2 &v2) are defined. Then, the operators "in" and "not_in" call those functions for working.
The implementation is in this repository:
https://github.com/ploncomi/python_like_cpp

How to get the value type from an output iterator?

Let's say that I have a C container (e.g., MyContainer) with contained objects stored as void* pointers. The only way to iterate through the elements of this container is via two interface functions:
getFirstElem(MyContainer const&, void*): Outputs the first element of the container.
getNextElem(MyContainer const&, void*): Outputs the next element of the container.
I want to code a generic function that iterates through the elements of this C container via the interface functions mentioned above and copy their values into a C++ container (e.g. std::vector).
What I've done so far:
template<typename OutputIterator>
void
copy_container(MyContainer const &cont, OutputIterator first) {
typename std::iterator_traits<OutputIterator>::value_type elem;
if(getFirstElem(cont, &elem)) {
do {
*first = elem;
++first;
} while(getNextElem(cont, &elem))
}
}
The above example works OK with normal iterators. However, it fails to compile with output iterators (e.g., copy_container(cont, std::back_inserter(myvector));).
The reason is that std::iterator_traits::value_type results in void in cases where the argument type is an output iterator.
Is there a way to make this generic function work for output iterators as well?
I know that in C++11 it could be done by using decltype (e.g., decltype(*first)), but I'm particularly interested in pre-C++11 solutions since I use an old C++ compiler (gcc v4.4.7).
As correctly observed, the value_type of an output iterator is void. So there not much to do apart from replacing this :
typename std::iterator_traits<OutputIterator>::value_type elem;
with this
decltype(*first) elem;
(even though the Standard doesn't guarantee it'll work - a proxy might be returned by dereferencing an output iterator).
As you said no C++11 solution so a redesign might be needed. Here are some options:
1. Pass the container
Instead of an iterator to the first element, you could pass a reference to the container. It seems like all you want is a push_back.
template<template<typename,typename> class stlContainer>
void copy_container(
MyMontainer const &cont, OutputIterator first)
{
// insertion in stlContainer
then all you need is a layer of traits to dispatch to the right implementation of insertion per container
2. Pass an extra template parameter
The value type could be an extra template parameter.
template<typename value_type, typename OutputIterator>
void copy_container(MyMontainer const &cont, OutputIterator first)
{
value_type elem;
...
You may use typetraits and specialization
template <typename IT>
struct it_value_type
{
typedef typename std::iterator_traits<IT>::value_type elem;
};
template <typename Container>
struct it_value_type<std::back_insert_iterator<Container>>
{
typedef typename Container::value_type elem;
};
template <typename Container>
struct it_value_type<std::front_insert_iterator<Container>>
{
typedef typename Container::value_type elem;
};
And then you code becomes:
template<typename OutputIterator>
void
copy_container(MyContainer const &cont, OutputIterator first) {
typename it_value_type<OutputIterator>::elem elem;
if (getFirstElem(cont, &elem)) {
do {
*first = elem;
++first;
} while (getNextElem(cont, &elem));
}
}
There are different ways to solve this, I did it like this:
template <class T>
std::enable_if_t<!std::is_same_v<typename T::container_type::value_type, void>, size_t> read(T first, size_t count)
{
typedef typename T::container_type::value_type value_type;
I have another one for the usual iterator.

typeid for container STL

I am writing a little template class which can get as template parameter list or vector (and the data type obviously).
I need to overload [ ] operator, to do this I want to use the overloaded [ ] of the vector and make a simple search (next, next, until we get to the desired index) for the list.
So I am checking with typeid if parameter is parameter of list and implements according to result like this:
const T* operator[](size_t _index)const
{
if(typeid(ContainerT<T,std::allocator<T> >) == typeid(vector<T>))
{
return m_container[_index];
}
else
{
const_iterator it = m_container.begin();
for(int i=0;i<_index;++i)
{
++it;
}
return *it;
}
}
If I dont use the [ ] for the list, everything is ok, but as I use it:
tContainer_t<int, list> list1;
cout<<list1[0]<<endl;
it is not compiling at all and here is the compilation error:
In file included from main.cpp:6:0:
tContainer.h: In member function ‘const T* tContainer_t<T, ContainerT>::operator[](size_t) const [with T = int, ContainerT = std::list, size_t = unsigned int]’:
main.cpp:68:9: instantiated from here
tContainer.h:80:29: error: no match for ‘operator[]’ in ‘((const tContainer_t<int, std::list>*)this)->tContainer_t<int, std::list>::m_container[_index]’
I dont understand since I checked that the typeid does work (well I think...) and anyway it seems the compiler sees that subscript will also be called for list.
Since the compiler needs to compile the entire function, even though one branch will not be taken at runtime (due to check), then you can't use this type of runtime checking to prevent the compilation error.
There are ways to do this involving using a helper function that can be specialized for the type. However, it can be complicated and in this case, not worth doing yourself, as the standard library has already done the work for you.
std::advance (located in header <iterator>) can be used to advance an iterator up to N times and is optimized for random-access iterators (like those returned by std::vector or std::deque) to do it in constant-time. Otherwise it falls back to steping one at a time using ++
// Note, changed signature to return const T&, which is more standard for operator[]
const T& operator[](size_t index) const
{
const_iterator itr = m_container.begin();
std::advance(itr, index);
return *itr;
}
Edit:
Assuming you wanted to learn how it's done, you would create the following functions, usually in a separate namespace. For now, I'm going to use the original intent of the question, and assume that you're using std::list or std::vector.
namespace helper
{
template <typename T, typename Alloc>
typename std::vector<T,Alloc>::const_reference
index_into(std::vector<T, Alloc> const& container, std::size_t index)
{
return container[index];
}
template <typename T, typename Alloc>
typename std::list<T,Alloc>::const_reference
index_into(std::list<T, Alloc> const& container, std::size_t index)
{
std::list<T, Alloc>::const_iterator itr = container.begin();
for(std::size_t i = 0; i < index; ++i)
{
++itr;
}
return *itr;
}
}
// Change your definition here
const T& operator[](size_t index) const
{
return helper::index_into(m_container, index);
}
Why this works: When you compile this with either std::list or std::vector, it uses function overloading to determine which of the two overloads of index_into to use. Hence, it only compiles the one that is legal, and doesn't attempt to compile both.
Note this, implementation only allows std::vector and std::list. If you wanted to allow any container, using std::advance is the generic and correct solution.
FYI, the implementation of std::advaance uses a similar technique. You can look at your implementation to see how it's done.
Use std::advance from <iterator>:
const T* operator[](size_t index) const
{
const_iterator it = m_container.begin();
std::advance(it, index);
return &*it;
}

std::vector and its iterator as single template typename

In order to get an "easier-to-remember" interface to the
index-generating function std::distance(a,b), I came up
with the idea of a better distinction of it's arguments
(when used against the base of a vector: vec.begin() )
by calling a templated function with the vector
and its iterator, like:
std::vector<MyType> vect;
std::vector<MyType>::const_iterator iter;
...
...
size_t id = vectorindex_of(iter, vect);
with the rationale of never confusing the order of
the arguments ;-)
The explicit formulation of the above idea would
read sth. like
template <typename T>
inline
size_t vectorindex_of(
typename std::vector<T>::const_iterator iter,
const std::vector<T>& vect ) {
return std::distance( vect.begin(), iter );
}
... which works but looks awkward.
I'd love to have the template mechanism implicitly deduce the types
like (pseudo-code):
template <typename T>
inline
size_t vectorindex_of(T::const_iterator iter, const T& vect) {
return std::distance( vect.begin(), iter );
}
... which doesn't work. But why?
The fix is easy: add typename before T::const_iterator iter. This is needed because class templates may be specialized and using typename tells the compiler a type name is expected at T::const_iterator and not a value or something.
You do the same in your less generic function, too.
template <typename T>
inline
std::size_t vectorindex_of(typename T::const_iterator iter, const T& vect) {
return std::distance( vect.begin(), iter );
}
Should work fine (notice the typename). Template arguments should be deduced in either case.
You might also be interested in the "easier-to-remember" way to get the index of a vector iterator:
i - vec.begin()
It's identical to pointer arithmetic with random access iterators!