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;
}