It is often considered a better style to access objects in generic code with free functions than with member functions because it allows one to
adapt an object to a template without modifying it just by adding the right overload of the free function. This is especially true with containers,
which can come in a wide variety (and some of them can’t even have member functions, for instance, C-style arrays).
Therefore, the C++ standard library provides free functions that can be applied on any standard container and that can be adapted for user-defined
containers. They are:
- Since C++11:
std::begin
, std::end
, std::cbegin
, std::cend
- Since C++14:
std::rbegin
, std::rend
, std::crbegin
, std::crend
- Since C++17:
std::size
, std::empty
, std::data
- Since C++20:
std::ssize
When writing generic code, you should prefer using those functions for objects that depend on the template arguments: it will allow your code to
work with a wider variety of containers.
Noncompliant code example
template<class T>
bool f(T const &t, std::vector<int> const &v) {
if (!t.empty()) { // Noncompliant in C++17
auto val = t.begin() // Noncompliant in C++11
->size(); // Noncompliant in C++17
return val == v.size(); // Compliant, v does not depend on a template parameter
}
return false;
}
Compliant solution
template<class T>
bool f(T const &t, std::vector<int> const &v) {
if (!std::empty(t)) { // Compliant
auto val = std::size(*std::begin(t)); // Compliant
return val == v.size();
}
return false;
}