简体   繁体   中英

How to implement own function to std::vector?

I would like to add a function that returns the .size() value as an integer, instead of unsigned integer.

Edit: Due to comments, i explain more detailed:

I have code:

int something = 3;
if(arr.size() > something) 

Which will produce compiler warning, and i dislike adding (int) to every place where i have this. So, a solution i thought it would be nice to have sizei() function:

int something = 3;
if(arr.sizei() > something) 

Which wouldnt produce a warning.

So, im not wanting to create a separate function, but a function in the std::vector itself.

Edit: Seems like the only way to do this is to create another function, such as:

template <typename T>
inline int sizei(const T &arr){
    return (int)arr.size();
}

On the positive side: this doesnt seem to increase my executable size at all.

First of all, why would you want that? I don't see any reason, or advantage:

Anyway, you can do this:

template<typename T>
int size(const std::vector<T> &v) { return (int) v.size(); }

//use
std::vector<int> ints;
//...
int count = size(ints);

Still I don't see any point in doing that. You can simply write:

int count = (int) ints.size(); 

But I would still say its not better than the following :

size_t count = ints.size(); //don't prefer anything over this. Always use size_t

Advice: avoid using int for size. Prefer size_t .


As for the edit in your question. Why don't you use size_t as:

size_t something = 3;
if(arr.size() > something) 

No warning. In my opinion, if you choose the data type consistently throughout your program, you wouldn't come across a situation when you've to compare int with size_t which is defined as unsigned integral type.

Or if there is some legacy code which you've to work with, and which use int for size, then I think its better to use explicit cast when you need it, instead of adding a function in the framework itself, which hides the potential problem:

int something = /*some legacy code API call or something */;
if(arr.size() > (size_t) something) 

//or even better;
size_t something = (size_t) /*some legacy code API call or something */;
if(arr.size() > something) 

As a rule, in C and C++ you should never use an unsigned type such as size_t to restrict the domain. That's because (1) these languages provide no range checking, and (2) they do provide unreasonable implicit promotions. No range checking means (1) no advantage, and unreasonable implicit promotions means (2) very undesirable disadvantages, so it's plain stupid thing to do: no advantage, very undesirable disadvantages.

However, the standard libraries for these languages do that. They do it for historical reasons only, caught irreversibly in early decisions which at one time made sense. This has both extremely silly consequences such as C99 requiring 17 (!) bits for ptrdiff_t , and it has the aforementioned extremely undesirable consequences such as using inordinately much time on hunting down bugs resulting from implicit promotions (etc.). For example, in C++ you are practically guaranteed that std::string( "bah!" ).length() < -5 – which can easily trip you up and anyway is as silly as it is possible to design.

Now, you can't infuse new member functions in std::vector , but you can add a freestanding function. A good name is countOf . Template it so that it can be applied to just about anything (raw arrays, vectors, etc.).

The triad of functions startOf , endOf and countOf were, as far as I know, first identified by Dietmar Kuehl. C++0x will have std::begin and std::end , but AFAIK no corresponding std::size . In the meantime you can just define this support, which allows you to treat any kinds of container plus raw arrays the same.

An example implementation & further discussion is provided at my blog .


EDIT Adding some code, because it's requested in the comments.

Detection of suitable iterator type:

template< typename Type >
struct It
{
    typedef typename Type::iterator T;
};

template< typename Type >
struct It< Type const >
{
    typedef typename Type::const_iterator T;
};

template< typename ElemType, Size N >
struct It< ElemType[N] >
{
    typedef ElemType* T;
};

And the countOf , startOf and endOf functions, using that deduced iterator type:

template< typename T >
inline Size countOf( T const& c )           { return static_cast<Size>( c.size() ); }

template< typename T, Size N >
inline Size countOf( T (&)[N] )             { return N; }

template< typename T >
inline typename It<T>::T startOf( T& c )    { return c.begin(); }

template< typename T, Size N >
inline T* startOf( T (&a)[N] )              { return a; }

template< typename T >
inline typename It<T>::T endOf( T& c )      { return c.end(); }

template< typename T, Size N >
inline T* endOf( T (&a)[N] )                { return a + N; }

where Size is a typedef for ptrdiff_t .

Note: in 64-bit Windows int (and even long ) is 32-bit. Hence, int is in general not sufficient for a really large array. ptrdiff_t is guaranteed to be able to represent the difference between any two pointers, when that difference is well-defined.


Cheers & hth.

I would favor using an explicit cast to int instead of a function: static_cast<int> (v.size()) . Even better would be to always use size_t when dealing with memory sizes. For example, favor for (size_t i=0; i < v.size(); ++i) over for (int i=0; i < (int) v.size(); ++i) . Use the right type for the job. You should not be comparing std::vector sizes with a signed type.

See the following references for why you should prefer size_t to int:

You can derive from vector as follows:

template<typename T>
class my_vector : public vector<T>
{
  // missing constructors!

  int size() const
  {
    if (vector<T>::size() > INT_MAX)
      throw std::range_error("too many elements in vector");
    return (int) vector<T>::size();
  }
};

The down-side is that you'll have to define and forward constructors yourself.

Quick answer for .size() is: no. For vectors, the possibilities are its storage value and the alloc method (default new / delete , not normally overridden) along with methods that utilize InputIterator .

Most are going to ask why would you want a different size_t . If it's just the annoy warnings, you can cast or use unsigned integers to iterate/check against size(). (If it's a lot of code, you going to have to find/replace)... If it is handling empty conditions, you could wrap the vector in a class with some smarts. As an aside, since I don't know your problem at hand, a good place to look for ideas and already implemented features is std library's algorithms such as sort , for_each , find , and lots more.

For std algorithms, see: http://www.sgi.com/tech/stl/table_of_contents.html

While @Nawaz, in my opinion, provided the most appropriate answer, if you really want to add an additional member to std::vector<> it isn't really possible. @zvrba provided the only way that could be accomplished, but as stated in the comments there the std container types do not have virtual destructors and therefore are not meant to be subclassed from.

However, you could implement a new type of vector using a container adaptor, like this:

template <class T>
class my_vector
{
public:
   int size_i() const
   {
      return static_cast<int>(container_.size());
   }

private:
   std::vector<T> container_;
};

The drawback here is that you have to explicitly expose the functions of the container that you actually need to support. If you are using 'std::vector' normally throughout your code, this would likely be a significant change. See 'std::queue' for an implementation example of a container adaptor.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM