Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it a good idea to extend std::vector?

Tags:

c++

stl

Working slightly with javascript, I realized it is ways faster to develop compared with C++ which slows down writing for reasons which often do not apply. It is not comfortable to always pass .begin() and .end() which happens through all my application.

I am thinking about extending std::vector (more by encapsulation than inheritance) which can mostly follow the conventions of javascript methods such as

.filter([](int i){return i>=0;})
.indexOf(txt2)
.join(delim)
.reverse()

instead of

auto it = std::copy_if (foo.begin(), foo.end(), std::back_inserter(bar), [](int i){return i>=0;} );

ptrdiff_t pos = find(Names.begin(), Names.end(), old_name_) - Names.begin();

copy(elems.begin(), elems.end(), ostream_iterator<string>(s, delim)); 

std::reverse(a.begin(), a.end());

But, I was wondering if it is a good idea, why already there is no C++ library for such common daily functionality? Is there anything wrong with such idea?

like image 643
vica Avatar asked Mar 02 '18 05:03

vica


People also ask

Is std::vector fast?

A std::vector can never be faster than an array, as it has (a pointer to the first element of) an array as one of its data members. But the difference in run-time speed is slim and absent in any non-trivial program. One reason for this myth to persist, are examples that compare raw arrays with mis-used std::vectors.

Should I use std for vector?

Here is a rule of thumb: If you want to add elements to your container or remove elements from your container, use a std::vector; if not, use a std::array. If you are busy, you can stop to read, if not, continue.

Can we extend vector?

If the increment is specified, Vector will expand according to it in each allocation cycle. Still, if the increment is not specified, then the vector's capacity gets doubled in each allocation cycle.

Is STD array faster than vector C++?

In both cases, the array version goes almost twice as fast as the vector version.


2 Answers

There's nothing inheritly wrong with this idea, unless you try to delete a vector polymorphically.

For example:

auto myvec = new MyVector<int>;
std::vector<int>* myvecbase = myvec;

delete myvecbase; // bad! UB
delete myvec; // ok, not UB

This is unusual but could still be a source of error.

However, I would still not recommend it.

To gain your added functionalities, you'd have to have an instance of your own vector, which means you either have to copy or move any other existing vectors to your type. It disallows you to use your functions with a reference to a vector.

For example consider this code:

// Code not in your control:
std::vector<int>& get_vec();

// error! std::vector doesn't have reverse!
auto reversed = get_vec().reverse();

// Works if you copy the vector to your class
auto copy_vec = MyVector<int>{get_vec()};
auto reversed_copy = copy_vec.reverse();

Also, it will work with only vector, whereas I can see the utility to have these functionalities with other container types.


My advice would be to make your proposed function free - not make them member of your child class of vector. This will make them work with any instance or references, and also overloadable with other container types. This will make your code more standard ( not using your own set of containers ) and much easier to maintain.

If you feel the need to implement many of those functional style utilities for container types, I suggest you to seek a library that implements them for you, namely ranges-v3, which is on the way to standardisation.


On the other side of the argument, there are valid use case for inheriting STL's class. For example, if you deal with generic code and want to store function object that might be empty, you can inherit from std::tuple (privately) to leverage empty base class optimization.

Also, it happened to me sometime to store a specific amount of elements of the same type, which could vary at compile time. I did extended std::array (privately) to ease the implementation.

However note something about those two cases: I used them to ease the implementation of generic code, and I inherited them privately, which don't expose the inheritance to other classes.

like image 143
Guillaume Racicot Avatar answered Sep 20 '22 12:09

Guillaume Racicot


A wrapper can be used to create a more fluent API.

template<typename container >
class wrapper{
public:
  wrapper(container const& c) : c_( c ){}

  wrapper& reverse() {
    std::reverse(c_.begin(), c_.end());
    return *this;
  }

  template<typename it>
  wrapper& copy( it& dest ) {
    std::copy(c_.begin(), c_.end(), dest ); 
    return *this;
  }

  /// ...

private:    
  container c_;
};

The wrapper can then be used to "beautify" the code

std::vector<int> ints{ 1, 2, 3, 4 };

auto w = wrapper(ints);    
auto out = std::ostream_iterator<int>(std::cout,", ");

w.reverse().copy( out );

See working version here.

like image 27
Thomas Avatar answered Sep 20 '22 12:09

Thomas