Sé que el siguiente código no se compilará.
void baz(int i) { }
void baz() { }
class Bar
{
std::function<void()> bazFn;
public:
Bar(std::function<void()> fun = baz) : bazFn(fun){}
};
int main(int argc, char **argv)
{
Bar b;
return 0;
}
Porque std::function
se dice que no considera la resolución de sobrecarga, como leí en esta otra publicación .
No entiendo completamente las limitaciones técnicas que forzaron este tipo de solución.
Leí sobre las fases de traducción y plantillas en cppreference, pero no puedo pensar en ningún razonamiento para el que no pueda encontrar un contraejemplo. Explicado a un medio laico (todavía nuevo en C ++), ¿qué y durante qué etapa de traducción hace que lo anterior no se compile?
c++
templates
translation
std-function
TuRtoise
fuente
fuente
Respuestas:
Esto realmente no tiene nada que ver con las "fases de traducción". Se trata únicamente de los constructores de
std::function
.Ver,
std::function<R(Args)>
no requiere que la función dada sea exactamente del tipoR(Args)
. En particular, no requiere que se le asigne un puntero de función. Puede tomar cualquier tipo invocable (puntero de función miembro, algún objeto que tenga una sobrecargaoperator()
) siempre que sea invocable como si tomaraArgs
parámetros y devuelva algo convertibleR
(o siR
es asívoid
, puede devolver cualquier cosa).Para ello, el constructor adecuado de
std::function
debe ser una plantilla :template<typename F> function(F f);
. Es decir, puede tomar cualquier tipo de función (sujeto a las restricciones anteriores).La expresión
baz
representa un conjunto de sobrecarga. Si usa esa expresión para llamar al conjunto de sobrecarga, está bien. Si usa esa expresión como parámetro de una función que toma un puntero de función específico, C ++ puede reducir el conjunto de sobrecarga a una sola llamada, lo que lo hace correcto.Sin embargo, una vez que una función es una plantilla y está utilizando la deducción de argumentos de plantilla para descubrir cuál es ese parámetro, C ++ ya no tiene la capacidad de determinar cuál es la sobrecarga correcta en el conjunto de sobrecarga. Por lo tanto, debe especificarlo directamente.
fuente
function
plantilla de clase es irrelevante . Lo que importa es el parámetro de plantilla en el constructor al que está llamando. Que es solotypename F
: aka, cualquier tipo.La resolución de sobrecarga ocurre solo cuando (a) está llamando el nombre de una función / operador, o (b) lo está enviando a un puntero (a función o función miembro) con una firma explícita.
Tampoco está ocurriendo aquí.
std::function
toma cualquier objeto que sea compatible con su firma. No toma un puntero de función específicamente. (una lambda no es una función estándar, y una función estándar no es una función lambda)Ahora en mis variantes de funciones homebrew, para la firma
R(Args...)
también acepto unR(*)(Args...)
argumento (una coincidencia exacta) exactamente por esta razón. Pero significa que eleva las firmas de "coincidencia exacta" por encima de las firmas "compatibles".El problema central es que un conjunto de sobrecarga no es un objeto C ++. Puede nombrar un conjunto de sobrecarga, pero no puede pasarlo "nativamente".
Ahora, puede crear un conjunto de pseudo-sobrecarga de una función como esta:
Esto crea un único objeto C ++ que puede hacer una resolución de sobrecarga en el nombre de una función.
Expandiendo las macros, obtenemos:
lo cual es molesto de escribir. Una versión más simple, solo un poco menos útil, está aquí:
tenemos una lambda que toma cualquier número de argumentos, luego los reenvía a ellos
baz
.Entonces:
trabajos. Diferimos la resolución de sobrecarga en el lambda que almacenamos
fun
, en lugar de pasarfun
un conjunto de sobrecarga directamente (que no puede resolver).Ha habido al menos una propuesta para definir una operación en el lenguaje C ++ que convierte un nombre de función en un objeto de conjunto de sobrecarga. Hasta que dicha propuesta estándar esté en el estándar, la
OVERLOADS_OF
macro es útil.Podría ir un paso más allá y admitir puntero de función de conversión compatible.
pero eso está empezando a ponerse obtuso.
Ejemplo en vivo .
fuente
El problema aquí es que no hay nada que le diga al compilador cómo hacer la función para decaer el puntero. Si usted tiene
Entonces el código funcionaría ya que ahora el compilador sabe qué función desea, ya que hay un tipo concreto al que está asignando.
Cuando usa
std::function
, llama a su constructor de objetos de función que tiene la formay como es una plantilla, debe deducir el tipo de objeto que se pasa. Como
baz
es una función sobrecargada, no hay un tipo único que pueda deducirse, por lo que la deducción de plantilla falla y se obtiene un error. Tendrías que usarpara forzar un solo tipo y permitir la deducción.
fuente
En el momento en que el compilador decide qué sobrecarga pasar al
std::function
constructor, todo lo que sabe es que elstd::function
constructor tiene la plantilla para tomar cualquier tipo. No tiene la capacidad de probar ambas sobrecargas y descubrir que la primera no se compila, pero la segunda sí.La forma de resolver esto es decirle explícitamente al compilador qué sobrecarga quieres con un
static_cast
:fuente