operator[]
and at()
¶vector::operator[]
has strong preconditionsvector::at()
has weaker preconditionsstd::out_of_range
{
vector<int> x{0, 1, 2};
REQUIRE_THROWS_AS(x.at(2), std::out_of_range);
}
{
vector<int> x{0, 1, 2};
REQUIRE_THROWS_AS(x.at(3), std::out_of_range);
}
Exercise 3.1 Write a table with representative values and expected results and a test for indexing.
{
vector<int> x = { 0, 1, 2, 3 };
cout << "size: " << x.size() << ", capacity: " << x.capacity() << endl;;
x.push_back(x.back()); // OK?
for (const auto& e : x) cout << e << " ";
}
vector::back()
is:T& back();
vector::push_back()
is:void push_back(const T&);
The expected preconditions of a const T&
argument to a function, which may alias a value being modified by the function, is:
a = a; // must be a no-op
Exercise 3.2 Extend the assignment test to validate self assignment for representative values.
A T&&
argument is more complex:
{
vector<string> x = { "Hello", "World" };
cout << "size: " << x.size() << ", capacity: " << x.capacity() << endl;;
x.push_back(move(x.front())); // OK?
for (const auto& e : x) cout << e << " ";
}
vector::push_back()
overload in this case is:void push_back(T&&);
Should this work?
clear()
¶vector::clear()
has the following postconditions:capacity()
unchanged{
vector<int> x = {0, 1, 2, 3};
auto n = x.capacity();
x.clear();
REQUIRE(x.empty());
REQUIRE(x.capacity() == n);
}
Exercise 3.3 Review the postconditions for your existing tests and make sure your tests are complete.
const
vector::begin()
{
int i = 0;
i += i++ + ++i;
cout << i << endl;
}
vector
are:{
vector<int> x = {0, 1, 2, 3};
x = move(x);
cout << x.size() << endl;
}
unique_ptr
reset(r.release())
x.reset(x.release())
{
auto x = make_unique<int>(42);
x = move(x);
cout << *x << endl;
}
{
string x = "Hello";
x = move(x);
cout << x.size() << endl;
}
{
vector<string> x = { "Hello", "World" };
cout << "size: " << x.size() << ", capacity: " << x.capacity() << endl;;
x.push_back(move(x.front())); // OK?
for (const auto& e : x) cout << e << " ";
}
vector
is resizedpush_back()
was:void push_back(T);
One argument is that for the basic interface, passing arguments by rvalue and lvalue references should be viewed as an optimization of passing by value and should not change behavior.
But that has performance implications.
noexcept
: Will not throw an exception{
vector<int> x{0, 1, 2};
auto copy = x;
REQUIRE_THROWS_AS(x.at(3), std::out_of_range);
REQUIRE(copy == x); // per basic exception guarantee
}
template <class T>
void test_vector_invariants(const T& x) {
REQUIRE(!(x.capacity() < x.size()));
REQUIRE((x.size() == 0) == x.empty());
REQUIRE(x.empty() == (x.begin() == x.end()));
//...
}
Exercise 3.4 Complete the invariant test for a vector and extend your tests to check the invariants after each mutating operation.