Constraints are not part of the signature, but parameters are. And constraints in parameters are enforced during overload resolution.
So let’s put the constraint in a parameter. It’s ugly, but it works.
class RequireStruct<T> where T : struct { }
class RequireClass<T> where T : class { }
static void Foo<T>(T a, RequireStruct<T> ignore = null) where T : struct { } // 1
static void Foo<T>(T? a) where T : struct { } // 2
static void Foo<T>(T a, RequireClass<T> ignore = null) where T : class { } // 3
(better six years late than never?)