4nt
4nt

Reputation: 407

Why does the compiler allow vector.begin()=vector.end() in C++?

While learning about iterators in C++ I tried the following:

#include <vector>

int main() {
    std::vector<int> a;
    a.end()=a.begin();
    //Why is this even allowed by the compiler?
}

What am I missing?

Upvotes: 8

Views: 783

Answers (5)

M.M
M.M

Reputation: 141628

The standard doesn't specify whether std::vector::iterator is a class type, or a raw pointer.

If it is a class type then this code calls operator= on the temporary object returned by a.end(). Not a very useful operation, but legal. (Rvalues may have functions called on them).

If your library makes std::vector::iterator be a pointer then this code would fail to compile, since simple assignment requires an lvalue on the left.

Jarod42 pointed out that if the iterator's assignment operator had been defined as:

std::vector::iterator& std::vector::iterator::operator =(std::vector::iterator) &;

then this code would be illegal; the trailing & means that the function is only selectable when it is being called on an lvalue.

But it wasn't defined that way, I would guess that the Committee didn't want to make some legal code illegal for no good reason; maybe there is a use case for it that nobody thought of yet.

Upvotes: 7

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275730

You cannot assign to an rvalue (aka temporary) primitive type in C++, like (int)7 or a pointer returned by-value from a function. This makes lots of sense, as the result of the assignment would be immediately discarded, so it is probably not what the programmer wanted. rvalue means 'right hand side of the = sign' from language grammars, or a temporary (anonymous) value.

However on objects of class type, = just invokes operator=. Usually when the lhs is an rvalue, this would make no sense, but sometimes it would (say, the class is a proxy object pseudo-reference (like what [] on std::vector<bool> returns). So it is treated nit at all special, it just invokes the method. And there was no way to tell if the method was invoked from a temporary rvalue or a 'real' lvalue.

rvalue references are new in C++11, as are rvalue qualified methods.

Prior to C++11, there was no way to block calling operator= on an object of class type just because it was a temporary.

As begin returns a non-const temporary, if it is of class type (not guaranteed by the standard) it can be assigned to.

Today we can block assignment to temporaries of class type, but the standard library has not been modified to block it on its types. Probably at least partly to avoid backwards compwtibility issues, and partly to determine best practices outside std first.

Upvotes: 2

Nipun Talukdar
Nipun Talukdar

Reputation: 5387

They are allowed because the iterator objects are copy-assignable and copy-constructible. That is why we can do something like a.begin() = a.end(). Also, we can do something auto it(a.begin()).

Check the examples below regarding copy-assignable.

#include <vector>

struct CopyConsAssignable
{
    int x;
    CopyConsAssignable(int a) 
    { 
        x = a; 
    }

    CopyConsAssignable(const CopyConsAssignable& other)
    {
        x = other.x;
    }

    CopyConsAssignable& operator=(const CopyConsAssignable& other)
    {
        if (this != &other)
        {
            x = other.x;
        }
        return *this;
    }
};


int main()
{
    std::vector<int> a;
    a.begin() = a.end();

    CopyConsAssignable(2) = CopyConsAssignable(4); // Allowed as the objects are copy-assignable
    return 0;
}

Upvotes: 1

Vlad from Moscow
Vlad from Moscow

Reputation: 311048

it would not be posiible if for example function end would return a pointer.

For example this code will not be compiled

int a[] = { 1, 2, 3 };

std::end( a ) = std::begin( a );

GCC issues error

error: lvalue required as left operand of assignment
  std::end( a ) = std::begin( a );
                ^

However when objects of class types are used then they can call the ovetloaded copy (or move) assignment operator.

So the question arises why do not the functions return constant iterator objects. I think that it would be possible to apply operator ++. For example

auto it = ++c.begin();

For direct access iterators as pointers you can write simply

auto it = begin( a ) + 1;

Upvotes: 10

Jarod42
Jarod42

Reputation: 217810

The line affect a temporary iterator, and so is useless.

std::vector::iterator = std::vector::iterator is allowed.

A way to disallow that would be to have

std::vector::iterator::operator =(std::vector::iterator) &; // note the extra &

but std::vector::iterator may be a simple pointer, and we can't disalow T* = T*

Upvotes: 3

Related Questions