How to get the address of the std::vector buffer start most elegantly?

后端 未结 11 2148
-上瘾入骨i
-上瘾入骨i 2020-12-03 18:12

I want to use std::vector for dynamically allocating memory. The scenario is:

int neededLength = computeLength(); // some logic here

// this will allocate t         


        
相关标签:
11条回答
  • 2020-12-03 18:19

    Actually, the main problem with &buffer[0] (note the absence of parantheses) isn't that it isn't really pretty. (That's subjective anyway. I remember finding buffer.begin(), buffer.end() not pretty at all, when I first learned to use the STL.)

    The main problem is that it invokes undefined behavior whenever buffer is empty -- and most code never checks for that. That's why I put these into my toolbox:

    template <class T, class TAl>
    inline T* begin_ptr(std::vector<T,TAl>& v)
    {return  v.empty() ? NULL : &v[0];}
    
    template <class T, class TAl>
    inline const T* begin_ptr(const std::vector<T,TAl>& v)
    {return  v.empty() ? NULL : &v[0];}
    
    template <class T, class TAl>
    inline T* end_ptr(std::vector<T,TAl>& v)
    {return v.empty() ? NULL : (begin_ptr(v) + v.size());} 
    
    template <class T, class TAl>
    inline const T* end_ptr(const std::vector<T,TAl>& v)
    {return v.empty() ? NULL : (begin_ptr(v) + v.size());}
    

    Using these, you can write your code as

    callFunction( begin_ptr(buffer), buffer.size() );
    

    Whether begin_ptr(buffer) is prettier than &buffer[0] is left for you to decide. However, given that NULL should be checked for every pointer function argument, it definitely is more safe.

    0 讨论(0)
  • 2020-12-03 18:20

    but this &(buffer[0]) looks ugly

    It’s the normal way. You can omit the parentheses, though:

    &buffer[0]
    
    0 讨论(0)
  • 2020-12-03 18:27

    As already said, no.

    The reason is that &buffer[0] is the only way guarantied by the standard to get the adresse of the vector buffer.

    0 讨论(0)
  • 2020-12-03 18:28

    Well, you can remove one set of parens:

    &buffer[0]
    

    but that is the common, idiomatic way of doing it. If it really offends you, I suppose you could use a template - something like:

    template <typename T> 
    T * StartOf( std::vector <T> & v ) {
        return &v[0];
    }
    
    0 讨论(0)
  • 2020-12-03 18:30

    For functions like these, I use a utility class, SizedPtr<T> that basically holds a pointer and an element count. A set of converter functions creates the SizedPtr<T> from different inputs. So the call changes to:

    vector<TCHAR> foo;
    callFunction(sizedptr(foo));
    

    One could even add an implicit std::vector constructor to SizedPtr, but I wanted to avoid this dependency.

    This helps only if callFunction is under your control. It is a pleasure to work with, if you work with different vector types in one application and you want to consolidate. If you generally work with std::vector, it's mostly pointless.

    Roughly:

    template<typename T>
    class SizedPtr
    {
        T * m_ptr;
        size_t m_size;
      public:
        SizedPtr(T* p, size_t size) : ... {}
        T * ptr() { return m_ptr; }
        size_t size() const { return m_size; }
    
       // index access, STL container interface, Sub-Sequence, ...
    
    }
    

    The idea behind this is to separate the operation - manipulating a contiguous sequence of elements - from the storage (std::vector). It's similar to what STL does with iterators, but avoids template infection.

    0 讨论(0)
  • 2020-12-03 18:32

    Elegant way would be to change callFunction or to write wrapper for it as follows:

    // legacy function
    void callFunction( TCHAR* buf, int buf_size)
    {
      // some code
    }
    
    // helpful template
    void callFunction( std::vector<TCHAR>::iterator begin_it, std::vector<TCHAR>::iterator end_it )
    {
      callFunction( &*begin_it, std::distance( begin_it, end_it ) );
    }
    
    // somewhere in the code
    int neededLength = computeLength();
    std::vector<TCHAR> buffer( neededLength );
    callFunction( buffer.begin(), buffer.end() );
    

    You could even make wrapper for all such functions (with different types, not only TCHAR):

    template<typename T>
    void callFunction( T begin_it, typename std::vector<typename T::value_type>::iterator end_it )
    {
      callFunction( &*begin_it, std::distance( begin_it, end_it ) );
    }
    

    Type T will be properly deduced (as std::vector<sometype>) and you'll be able still write callFunction( buffer.begin(), buffer.end() );.

    Note that you cannot declare template function as void callFunction( typename std::vector<typename T::value_type>::iterator begin_it, typename std::vector<typename T::value_type>::iterator end_it ) as someone proposed recently as an edit to this answer, because in that case you will get the deducion error.

    0 讨论(0)
提交回复
热议问题