1

I am coding my very own vector container. To do so, I have a vector class and a RAIterator (Random Access Iterator) class. In my vector class I am implementing all of std::vector member functions. Amongst them is begin() which returns an iterator to the first element of the vector. There is also an overload which returns a constant iterator. My functions look like this:

iterator        begin() { return (iterator(_data)); }
const_iterator  begin() const { return (const_iterator(_data)); }

Here iterator and const_iterator are the same as in iterator_traits. My main looks like this:

int main(void)
{
    ft::vector<int> v;

    ft::vector<int>::iterator it2 = v.begin();
    ft::vector<int>::const_iterator it = v.begin();
}

ft is the namespace I am using to create my vector. The first call to v.begin() is alright but the second is not. This is because the first begin() function is called both times and since it returns iterator, the compiler tells me that there is no available conversion from iterator to const_iterator. Here is the actual error I receive:

main.cpp:70:34: error: no viable conversion from 'RAIterator<ft::vector<int, std::__1::allocator<int> >::pointer>' to
      'RAIterator<ft::vector<int, std::__1::allocator<int> >::const_pointer>'
        ft::vector<int>::const_iterator it = v.begin();
                                        ^    ~~~~~~~~~
./iterators/RAIterator.hpp:49:9: note: candidate constructor not viable: no known conversion from 'ft::vector<int,
      std::__1::allocator<int> >::iterator' (aka 'RAIterator<int *>') to 'const int *' for 1st argument
        RAIterator(T src) : _ptr(src) { /*std::cout << "Second constructor called" << std::endl;*/ }
        ^
./iterators/RAIterator.hpp:50:9: note: candidate constructor not viable: no known conversion from 'ft::vector<int,
      std::__1::allocator<int> >::iterator' (aka 'RAIterator<int *>') to 'const ft::RAIterator<const int *> &' for 1st argument
        RAIterator(const RAIterator &src) : _ptr(src._ptr) { /*std::cout << "Third constructor called" << std::endl;*/ } // CHANGE
        ^
1 error generated.

Here are my RAIterator class constructors:

RAIterator() : _ptr(NULL) { /*std::cout << "First constructor called" << std::endl;*/ }
RAIterator(T src) : _ptr(src) { /*std::cout << "Second constructor called" << std::endl;*/ }
RAIterator(const RAIterator &src) : _ptr(src._ptr) { /*std::cout << "Third constructor called" << std::endl;*/ } // CHANGE

I don't know how to solve this problem, i.e. how to make this conversion possible. If I write std instead of ft it compiles correctly, so I have to be able to support this conversion.

P.S. I have to use C++98. Weird, right?

kubo
  • 221
  • 1
  • 8

2 Answers2

2

You can add a converting constructor.

In C++98 there aren't all the helpful traits to only allow the template constructor when T is pointer to const, and U is the corresponding pointer to mutable, so it will always participate in overload resolution, but instantiating it will fail for incompatible pointer types.

template<typename T>
class RAIterator {
    T _ptr;
public:
    RAIterator() : _ptr(nullptr) {}
    RAIterator(T src) : _ptr(src) {}
    RAIterator(const RAIterator &src) : _ptr(src._ptr) {}
    template<typename U>
    // requires std::same_as<std::remove_pointer_t<T>, const std::remove_pointer_t<U>>
    RAIterator(const RAIterator<U> &src) : _ptr(src._ptr) {}

    // all your existing stuff
};
Caleth
  • 52,200
  • 2
  • 44
  • 75
1

C++ does not support function overloading based on return types, only on argument types (where this is also considered an argument). So there is no way for the compiler to distinguish the two v.begin() calls.

If you call begin() on a const object, you'll see that it calls the const overload:

    ft::vector<int> const &const_v = v;
    ft::vector<int>::const_iterator it = const_v.begin();

But that is a bit silly, of course.

In C++11, cbegin and cend were added to make this easier.

But why does it work unmodified in std::vector? I can't find any documentation on this, but I think the reason must be that std::vector::iterator supports implicit conversion to std::vector::const_iterator.

Thomas
  • 174,939
  • 50
  • 355
  • 478
  • Thank you. How could I code this implicit conversion? – kubo Oct 11 '22 at 10:30
  • Either as an implicit conversion operator on `iterator` or as an implicit conversion constructor on `const_iterator`. You can do a web search for these terms. – Thomas Oct 11 '22 at 10:31
  • The think is that I do not have an iterator class or a const_iterator class. I just have a RAIterator class which is a template and the parameter I pass onto the template is either iterator or const_iterator – kubo Oct 11 '22 at 10:37
  • Then add an implicit conversion from `RAIterator` to `RAIterator`. Some template trickery might be needed. Or open a new question with the relevant code if you can't figure it out. – Thomas Oct 11 '22 at 10:38