I know you asked for C++, but At least for Java we do not honor TTL. (because the JVM doesn't surface it to us). If you implement your own NameResolver (not as hard as it sounds!) you can honor these TTLs.
I believe C++ uses the cares resolver which IIRC can resort to doing TCP lookups if the response size is too large. Alas, I cannot answer with any more detail.
gRPC has the option to do health checks, but what I think you actually want are keep-alives. This is configurable on the channel and the server. If you can add more detail about the problem you are trying to avoid, I can give a better answer.
As for if DNS is a really bad idea: Not really. It has issues, but none of them are particularly damning. For example, when you add a new server most clients won't find out about it until they poll again. gRPC is designed around a push based name resolution model, with clients being told what servers they can talk to. DNS is adapted onto this model, by periodically spawning a thread and notifying the client via the push-interface.
The DNS support is pretty good in gRPC, to the point that implementing a custom DNS resolver is likely to cause more issues (what happens if the A lookups succeed, but the AAAA fail?, what happens if there are lots of addresses for a single endpoint?, etc.)
One last thing to consider: the loadbalancer in gRPC is independent of the name resolver. You could continue to use DNS (and do SRV lookups and such) and pass that info into your own custom client-side LB. This is what gRPCLB does, but you could customize your own copy to not depend on a gRPCLB server. There's lots of options here.