¿Cómo funciona la resolución de sobrecarga cuando un argumento es una función sobrecargada?

Preámbulo

La resolución de sobrecarga en C ++ puede ser un proceso demasiado complejo. Se necesita un gran esfuerzo mental para comprender todas las reglas de C ++ que rigen la resolución de sobrecarga. Recientemente se me ocurrió que la presencia del nombre de una función sobrecargada en la lista de argumentos puede aumentar la complejidad de la resolución de sobrecarga. Dado que resultó ser un caso ampliamente utilizado, publiquéuna pregunta y recibí una respuesta que me permitió comprender mejor la mecánica de ese proceso. Sin embargo, la formulación de esa pregunta en el contexto de iostreams parece haber distraído un poco el enfoque de las respuestas desde la esencia misma del problema que se está abordando. Así que comencé a profundizar y encontré otros ejemplos que piden un análisis más elaborado del problema. Esta pregunta es introductoria y es seguida por ununo más sofisticado.

Pregunta

Suponga que uno entiende completamente cómo funciona la resolución de sobrecarga en ausencia de argumentos que sean nombres de funciones sobrecargadas. ¿Qué enmiendas deben hacerse a su comprensión de la resolución de sobrecarga, de modo que también cubra los casos en que las funciones sobrecargadas se usan como argumentos?

Ejemplos

Dadas estas declaraciones:

void foo(int) {}
void foo(double) {}
void foo(std::string) {}
template<class T> void foo(T* ) {}

struct A {
    A(void (*)(int)) {}
};

void bar(int x, void (*f)(int)) {}
void bar(double x, void (*f)(double)) {}
void bar(std::string x, void (*f)(std::string)) {}
template<class T> void bar(T* x, void (*f)(T*)) {}
void bar(A x, void (*f2)(double)) {}

Las siguientes expresiones dan como resultado la siguiente resolución del nombrefoo (al menos con gcc 5.4):

bar(1, foo); // foo(int)
             // but if foo(int) is removed, foo(double) takes over

bar(1.0, foo); // foo(double)
               // but if foo(double) is removed, foo(int) takes over

int i;
bar(&i, foo); // foo<int>(int*)

bar("abc", foo); // foo<const char>(const char*)
                 // but if foo<T>(T*) is removed, foo(std::string) takes over

bar(std::string("abc"), foo); // foo(std::string)

bar(foo, foo); // 1st argument is foo(int), 2nd one - foo(double)
Código para jugar:
#include <iostream>
#include <string>

#define PRINT_FUNC  std::cout << "\t" << __PRETTY_FUNCTION__ << "\n";

void foo(int)                      { PRINT_FUNC; }
void foo(double)                   { PRINT_FUNC; }
void foo(std::string)              { PRINT_FUNC; }
template<class T> void foo(T* )    { PRINT_FUNC; }

struct A { A(void (*f)(int)){ f(0); } };

void bar(int         x, void (*f)(int)        ) { f(x); }
void bar(double      x, void (*f)(double)     ) { f(x); }
void bar(std::string x, void (*f)(std::string)) { f(x); }
template<class T> void bar(T* x, void (*f)(T*)) { f(x); }
void bar(A, void (*f)(double)) { f(0); }

#define CHECK(X) std::cout << #X ":\n"; X; std::cout << "\n";

int main()
{
    int i = 0;
    CHECK( bar(i, foo)                     );
    CHECK( bar(1.0, foo)                   );
    CHECK( bar(1.0f, foo)                  );
    CHECK( bar(&i, foo)                    );
    CHECK( bar("abc", foo)                 );
    CHECK( bar(std::string("abc"), foo)    );
    CHECK( bar(foo, foo)                   );
}

Respuestas a la pregunta(1)

Su respuesta a la pregunta