2

I building a simple class that is supposed to mimic the functionality of the std::string class (as an exercise!):

#ifndef _STR12_1_H
#define _STR12_1_H

#include <string>
#include <iostream>

class Str12_1
{
public:

    typedef char* iterator;
    typedef const char* const_iterator;
    typedef long size_type;


    Str12_1();
    Str12_1(const Str12_1& str);
    Str12_1(const char *p);
    Str12_1(const std::string& s);

    size_type size() const;

    //Other member functions


private:
    iterator first;
    iterator onePastLast;
    iterator onePastAllocated;
};

In order to avoid the overhead associated with "new" (and to increase my familiarity with the <memory> header), i've opted to use the library's allocator template class to allocate memory for my string. Here is an example of my use of it in the copy constructor:

#include <memory>
#include <algorithm>

using std::allocator;
using std::raw_storage_iterator;
using std::uninitialized_copy;


Str12_1::Str12_1(const Str12_1& str)
{
    allocator<char> charAlloc;
    first = charAlloc.allocate(str.size());
    onePastLast = onePastAllocated = first + str.size();
    *onePastLast = '\0';

    raw_storage_iterator<char*, char> it(first);

    uninitialized_copy(str.first, str.onePastLast, it);


}

The compiler keeps telling me two errors on the "uninitialized_copy" line which both lead back to headers in the library, :

error: invalid conversion from 'char' to 'char*'

error: no match for 'operator!=' in '__first != __last'

The problem is that I don't understand where on that line the conversion from char to char* is, and why two pointers of the same type (str.first, str.onePastLast) cannot be compared with "!=".

I could use "new", but as stated before, I want to get practice with <memory>. So can someone tell me why this isn't working?

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
Kevin
  • 2,617
  • 29
  • 35
  • 2
    The default allocator just calls `new` under the hood. There's no magic in it. It's there to allow the user to customize the memory allocation strategy, by supplying a different allocator. But the default one just does the same thing as `new`. Just thought you might want to know that, even if it doesn't answer your question :) – jalf Feb 01 '11 at 22:02
  • Are you sure? What I gathered from Accelerated C++ and every other resource online is that "new" calls the type's default constructor after it allocates space, while ".allocate" does not. – Kevin Feb 01 '11 at 22:28
  • 1
    [Clearly the problem is your bad include guard identifier](http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier). :) – GManNickG Feb 01 '11 at 22:28
  • 1
    @Kevin: Yes, but for a primitive `char`, `new char[x];` does nothing, and only `new char[x]()` zeroes out the block. - As to copying the string, `strcpy` and the like are probably better, since `uninitialized_copy` has a rather specific task (invoke the constructor to copy complicated types into uninitialized memory) which is not necessary for a primitive type. – UncleBens Feb 01 '11 at 22:32
  • @GMan: Ha! Yes... that's generated by NetBeans automatically whenever I create a new header file, and I was just too lazy to change it! I knew someone would mention it though. – Kevin Feb 01 '11 at 22:34
  • @UncleBens: Ah! Understood. I would still like to understand why exactly my call isn't working. Have any idea? – Kevin Feb 01 '11 at 22:39

1 Answers1

5

Looking at the standard raw_storage_iterator does not typedef value_type to be T, but it's void instead:

template <class OutputIterator, class T>
class raw_storage_iterator
: public iterator<output_iterator_tag,void,void,void,void>
                                      ^^^^

whereas uninitialized_copy has to use that typedef:

template <class InputIterator, class ForwardIterator>
ForwardIterator uninitialized_copy(InputIterator first, InputIterator last,
ForwardIterator result);

Effects:

for (; first != last; ++result, ++first)
::new (static_cast<void*>(&*result))
typename iterator_traits<ForwardIterator>::value_type(*first);
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

In your code, after all substitutions, this leads to:

new (...&*result) void (*first);
                  ^^^^^^^^^^^^^
                 invalid use here

From that you can conclude that those two were never meant to work together.

If you want to use raw_storage_iterator, then it should be fine to pass it to std::copy since all the magic happens in the operator=(const T&) overload.

If you think any of this is necessary for a primitive like char where you might just allocate with new char[x] (NB! terminating NUL) and copy with strcpy.

UncleBens
  • 40,819
  • 6
  • 57
  • 90
  • Ah! This is exactly what i'm looking for. Thanks. (What's weird is that it does not compile in GCC, but does with MS's Visual C++ compiler. – Kevin Feb 01 '11 at 23:15
  • With VC++ it doesn't compile for me for the second error you get, and it pretty much tells me that `uninitialized_copy` is "deprecated" anyway. – UncleBens Feb 01 '11 at 23:26