You're using begin as an unqualified name, and unqualified name lookup
... examines the scopes as described below, until it finds at least one declaration of any kind, at which time the lookup stops and no further scopes are examined.
reference.
From your member function's perspective, the first scope providing a name begin is the class scope, so it populates the overload set from that scope, and stops looking.
Only after this name lookup stage, does it try to choose one of the overload set, and decide nothing matches. The compiler doesn't then go back and start searching from the next scope, it just gives up.
Your options are:
use the existing container member function instead of the free function (this is slightly less verbose than the explicitly-qualified version below)
auto begin() { return v.begin(); }
use qualified names instead
auto begin() { return ::std::begin(v); }
add the correct overload to class scope, with using std::begin;
Ignore that one, I forgot you can't introduce non-member names at class scope with using.
inject the correct overload into the narrower scope of the member function body itself, so the search stops there
auto begin() { using std::begin; return begin(v); }
stop providing a begin member function in the first place, and instead add a non-member begin overload to the enclosing namespace. This is more modern and avoids the lookup problem.
namespace N {
struct S { std::vector<int> v; };
std::vector<int>::iterator begin(S& s) { return s.v.begin(); }
// and end, cbegin, etc. etc.
}