Advice on a Better Way to Extend C++ Stl Container with User-Defined Methods

Advice on a better way to extend C++ STL container with user-defined methods

why you need extend vector in this way?

use standard <algorithm> with your functors.

e.g.

std::min_element, std::max_element

int max_a = std::max_element
(
v.begin(),
v.end(),
boost::bind(
std::less< int >(),
bind( &Item::a, _1 ),
bind( &Item::a, _2 )
)
)->a;

std::accumulate - for calculate avarage

const double avg_c = std::accumulate( v.begin(), v.end(), double( 0 ), boost::bind( Item::c, _1 ) ) / v.size(); // ofcourse check size before divide  

your ItemList::SpecialB() could be rewrited as:

int accumulate_func( int start_from, int result, const Item& item )
{
if ( item.SpecialB() < start_from )
{
result -= item.SpecialB();
}
return result;
}

if ( v.empty() )
{
throw sometghing( "empty vector" );
}
const int result = std::accumulate( v.begin(), v.end(), v.front(), boost::bind( &accumulate_func, v.front(), _1, _2 ) );

BTW: if you don't need access to members, you don't need inheritance.

How can I extend stl classes to have my own methods?

You should use free functions:

template<typename... Args>
bool has_key(std::map<Args...> const& map, typename std::map<Args...>::key_type key) {
return map.find(key) != map.end();
}

Don't use inheritance if it's not necesarry. That's silly.

Extension of STL container through composition or free functions?

Hashing is an algorithm not a type, and probably shouldn't be restricted to data in any particular container type either. If you want to provide hashing, it probably makes the most sense to create a functor that computes a hash one element (int, as you've written things above) at a time, then use std::accumulate or std::for_each to apply that to a collection:

namespace whatever { 
struct hasher {
int current_hash;
public:
hasher() : current_hash(0x1234) {}

// incredibly simplistic hash: just XOR the values together.
operator()(int new_val) { current_hash ^= new_val; }
operator int() { return current_hash; }
};
}

int hash = std::for_each(coll.begin(), coll.end(), whatever::hasher());

Note that this allows coll to be a vector, or a deque or you can use a pair of istream_iterators to hash data in a file...

Adding specialized functionality to STL containers

I think the immediate cause of your error is that you need typename:

typename std::list<T>::iterator i, e = this.short_list.end();

... because the compiler doesn't realize that iterator is a type yet.

But you really don't want to derive from std::list, and you don't want to write your own sort.

Algorithmic efficiency of each C++ STL collection operations

Most online references include such data (other than benchmarks, which I believe aren't very useful for most people, if not done by themselves).

For example, look at http://en.cppreference.com/w/

It has a 'Complexity' field for most methods.

Is it okay to inherit implementation from STL containers, rather than delegate?

The risk is deallocating through a pointer to the base class (delete, delete[], and potentially other deallocation methods). Since these classes (deque, map, string, etc.) don't have virtual dtors, it's impossible to clean them up properly with only a pointer to those classes:

struct BadExample : vector<int> {};
int main() {
vector<int>* p = new BadExample();
delete p; // this is Undefined Behavior
return 0;
}

That said, if you're willing to make sure you never accidentally do this, there's little major drawback to inheriting them—but in some cases that's a big if. Other drawbacks include clashing with implementation specifics and extensions (some of which may not use reserved identifiers) and dealing with bloated interfaces (string in particular). However, inheritance is intended in some cases, as container adapters like stack have a protected member c (the underlying container they adapt), and it's almost only accessible from a derived class instance.

Instead of either inheritance or composition, consider writing free functions which take either an iterator pair or a container reference, and operate on that. Practically all of <algorithm> is an example of this; and make_heap, pop_heap, and push_heap, in particular, are an example of using free functions instead of a domain-specific container.

So, use the container classes for your data types, and still call the free functions for your domain-specific logic. But you can still achieve some modularity using a typedef, which allows you to both simplify declaring them and provides a single point if part of them needs to change:

typedef std::deque<int, MyAllocator> Example;
// ...
Example c (42);
example_algorithm(c);
example_algorithm2(c.begin() + 5, c.end() - 5);
Example::iterator i; // nested types are especially easier

Notice the value_type and allocator can change without affecting later code using the typedef, and even the container can change from a deque to a vector.

Is there a better way to check if a STL container is a multi* container

Seeing as there are a small finite number of std::multi* containers, you can just list them:

#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>

#include <type_traits>

template <typename Container>
struct is_multi_container :
std::false_type
{};

template <typename T, typename Compare, typename Alloc>
struct is_multi_container<std::multiset<T, Compare, Alloc>> :
std::true_type
{};

template <typename T, typename Compare, typename Alloc>
struct is_multi_container<std::multimap<T, Compare, Alloc>> :
std::true_type
{};

template <typename T, typename Compare, typename Alloc>
struct is_multi_container<std::unordered_multiset<T, Compare, Alloc>> :
std::true_type
{};

template <typename T, typename Compare, typename Alloc>
struct is_multi_container<std::unordered_multimap<T, Compare, Alloc>> :
std::true_type
{};

More lines of code, but it's easy to read and is direct in its reasoning (i.e., it definitely works!).

Being an explicit list, the caveat is that it doesn't extend itself automatically. For that, your solution is good. C++14 may have an AssociativeContainer concept which would make this even easier; research on this is left as an exercise for the reader. ;)

Example:

#include <iostream>
#include <iomanip>

int main()
{
std::cout << std::boolalpha;

#define TEST(type, ...) \
std::cout << type " is: " \
<< is_multi_container<__VA_ARGS__>::value \
<< std::endl

TEST("std::set<T>", std::set<int>);
TEST("std::multiset<T>", std::multiset<int>);

TEST("std::map<K,T>", std::map<int, double>);
TEST("std::multimap<K,T>", std::multimap<int, double>);

TEST("std::unordered_set<T>", std::unordered_set<int>);
TEST("std::unordered_multiset<T>", std::unordered_multiset<int>);

TEST("std::unordered_map<K,T>", std::unordered_map<int, double>);
TEST("std::unordered_multimap<K,T>", std::unordered_multimap<int, double>);
}

Output:

std::set<T> is: false
std::multiset<T> is: true
std::map<K,T> is: false
std::multimap<K,T> is: true
std::unordered_set<T> is: false
std::unordered_multiset<T> is: true
std::unordered_map<K,T> is: false
std::unordered_multimap<K,T> is: true

Adding custom methods to std::vector or typdef

You can't do that: C++ doesn't have extension methods similar to what you have in C#.

You could derive your class from vector, but then you have to change all the client code. Besides, as Roddy points out, std::vector doesn't have a virtual destructor, so deriving from it is a bad idea in general.

IMO, writing a simple function would be much better choice. Additional benefit is that it would also work for most other containers (e.g. std::list) and be more compatible with most of algorithms in STL which are also typically free functions.

So you would have this instead:

myNewSearchFunction(v, "ivor"); // instead of v.myNewSearchFunction("ivor");

Internally, this function does std::find_if(v.begin(), v.end(), ....

BTW, please note that it would be better to use std::begin(v), std::end(v) than v.begin(), v.end(). This lets you run the same code on e.g. arrays.

Putting things into classes isn't always the best choice in C++.

C++ extend a vector with another vector

From here

// reserve() is optional - just to improve performance
v.reserve(v.size() + distance(v_prime.begin(),v_prime.end()));
v.insert(v.end(),v_prime.begin(),v_prime.end());


Related Topics



Leave a reply



Submit