std::copy results in "Expression cannot seek value-initialized vector iterator"

Apologies in advance for the Vehicle class. There's no good reason why it's templated. I was just curious how to create a templated class that can be appended to an ostream.

The crux of the problem is that I want to use std::copy to copy Vehicle objects from a populated vector (cars) to an empty vector (copy_cars).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Compiled using MSVC
#include <iostream>
#include <vector>
#include <sstream>

using namespace std;

template <class T>
struct Vehicle 
{ 
    T num = -1; 
    Vehicle(T n) : num(n) {};

    template <class T>
    friend std::ostream& operator<<(std::ostream& stream, const Vehicle<T>& b);
};

template <class T>
std::ostream& operator<<(std::ostream& stream, const Vehicle<T>& b)
{
    stream << "[Veh:" << b.num << "]";
    return stream;
}

int main()
{
    // cars will contain Veh[1], Veh[5], Veh[3], in that order.
    vector<Vehicle<double>> cars; 
    cars.push_back(Vehicle<double>(1));
    cars.emplace_back(3);
    cars.emplace(cars.begin()+1, 5); 

    vector<Vehicle<double>> copy_cars;
    // Copy Veh[5], Veh[3] (in that order) to copy_cars. 
    copy(cars.begin()+1, cars.end(), copy_cars.begin());

    for (const auto car : copy_cars)
    {
        cout << car << ", ";
    }
    cout << "\n";
    return 0;
}


The issue is with the third argument to copy(..., copy_cars.begin()) which is the reason for the error:
Expression: cannot seek value-initialized vector iterator


If I replace copy_cars.begin() with back_inserter(copy_cars), per advice from [1], the code works as expected. What is copy() expecting as the third argument and why isn't copy_cars.begin() suitable?

[1] https://stackoverflow.com/questions/57248465/cannot-seek-value-initialized-vector-iterator
Last edited on
In pseudocode, std::copy is basically just doing this:

1
2
3
4
5
copy( first, last, dest_first )
{
    while( first != last ) *dest_first++ = *first++;
    return dest_first;
}

Note that it does not allocate additional memory in the destination container but expects the memory to already be allocated. If you first sized the destination container to be the correct size it would work. Alternatively, by passing back_inserter(...) the iterator will be such that the *dest_first operation will extend the size of the container before copying the source element.
Last edited on
@DizzyDon

Apparently, reserving storage (increasing a vector's capacity) via vector.reserve() isn't sufficient. For std::copy to work, there needs to be elements in the vector.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <vector>

using std::vector;
using std::ostream;
using std::cout;

struct Foo 
{
    int num; 
    Foo() : num(-1) {}
    Foo(int t) : num(t) {}
    friend ostream& operator<<(ostream& stream, const Foo& b);
};

ostream& operator<<(ostream& stream, const Foo& b)
{
    stream << "[Foo: " << b.num << "]";
    return stream;
}

int main()
{
    vector<Foo> v0 {Foo(1), Foo(5), Foo(3)};
    cout << "v0.size: " << v0.size() << "\n";
    cout << "v0.capacity: " << v0.capacity() << "\n\n";
    
    vector<Foo> v1(v0.size()); // Ok
    //vector<Foo> v1;
    //v1.reserve(v0.size()); // Not Ok. 
    cout << "v1.size (before copy): " << v1.size() << "\n";
    cout << "v1.capacity (before copy): " << v1.capacity() << "\n";
    cout << "v1 items (before copy): " << "\n";
    for (const auto item : v1) cout << item << " ";
    cout << "\n\n";
    
    copy(v0.begin() + 1, v0.end(), v1.begin() + 1);
    cout << "v1.size (after copy): " << v1.size() << "\n";
    cout << "v1 items (after copy): " << "\n";
    for (const auto item : v1) cout << item << " ";
    
    return 0;
}


Output:
1
2
3
4
5
6
7
8
9
10
11
12
v0.size: 3
v0.capacity: 3

v1.size (before copy): 3
v1.capacity (before copy): 3
v1 items (before copy):
[Foo: -1] [Foo: -1] [Foo: -1]

v1.size (after copy): 3
v1 items (after copy):
[Foo: -1] [Foo: 5] [Foo: 3]


To address my original post, I needed to default-insert elements in the destination vector:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template <class T>
struct Vehicle
{
    T num = -1;
    Vehicle() {} // Define default constructor
    Vehicle(T n) : num(n) {}

    template<class T>
    friend ostream& operator<<(ostream& stream, const Vehicle<T>& b);
};

...

int main()
{
    ...
    vector<Vehicle<double>> copy_cars(5);
    copy(cars.begin() + 1, cars.end(), copy_cars.begin());
    ...
    return 0;
}
Last edited on
@ElusiveTau,
You are using the wrong vector member function.

Use resize(), not reserve().
@lastchance

Thanks. Indeed, resize() is needed (which also insert default-elements).
However, one cannot always create "empty" elements (types with no default ctor).
If only one could have a "smart iterator" that does "push_back" ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <vector>

using std::vector;
using std::ostream;
using std::cout;

struct Foo 
{
    int num; 
    Foo() : num(-1) {}
    Foo(int t) : num(t) {}
    friend ostream& operator<<(ostream& stream, const Foo& b);
};

ostream& operator<<(ostream& stream, const Foo& b)
{
    stream << "[Foo: " << b.num << "]";
    return stream;
}

int main()
{
    vector<Foo> v0 {Foo(1), Foo(5), Foo(3)};
    vector<Foo> v1;
    copy( v0.begin(), v0.end(), std::back_inserter(v1) ); // https://www.cplusplus.com/reference/iterator/back_inserter/
    cout << "v1.size: " << v1.size() << "\n";
    cout << "v1.capacity: " << v1.capacity() << "\n\n";
    for (const auto item : v1) cout << item << " ";
}
Why not construct v1 direct from v0?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <vector>

using std::vector;
using std::ostream;
using std::cout;

struct Foo {
    int num {-1};

    Foo(int t = -1) : num(t) {}

    friend ostream& operator<<(ostream& stream, const Foo& b) {
        return stream << "[Foo: " << b.num << "]";
    }
};

int main() {
    vector<Foo> v0 {Foo(1), Foo(5), Foo(3)};
    vector<Foo> v1 {v0.begin() + 1, v0.end()};

    cout << "v1.size: " << v1.size() << "\n";
    cout << "v1.capacity: " << v1.capacity() << "\n\n";

    for (const auto& item : v1)
        cout << item << " ";
}



v1.size: 2
v1.capacity: 2

[Foo: 5] [Foo: 3]

Topic archived. No new replies allowed.