namespace c++ c++11 language-lawyer std-function c++14

c++ - namespace - ¿Es ilegal invocar una std:: function<void(Args…)> bajo el estándar?



std::cout (1)

Sí, tu análisis es correcto; Llegué a la misma conclusión here .

Según Daniel Kruegler, este problema debería aparecer en la lista de defectos de la biblioteca después del siguiente envío :

Ya se ha enviado un problema de biblioteca correspondiente, pero aún no está visible en la lista de problemas.

Esperemos que una vez que se haga visible también tengamos una respuesta concluyente sobre si es posible construir una std::function con la firma devolviendo el void pasando una llamada con la firma devolviendo el no-vacío ( Usando `std :: function <void (.. .)> `para llamar a la función no nula ).

Actualización: esto se ingresó como LWG 2420 , que se resolvió a favor del tipo de retorno de void caja especial para static_cast el resultado de la función invocada a void . Esto significa que un invocable que devuelve un no- void puede ser el objetivo de un std::function<void(...)> . LWG2420 se aplicó como una corrección posterior a la publicación a C ++ 14; Mientras tanto, todos los compiladores que conozco aplican efectivamente este comportamiento como una extensión en el modo C ++ 11.

Todas las citas son de N3797 .

4/3 [conv]

Una expresión e puede convertirse implícitamente a un tipo T si y solo si la declaración T t = e; Está bien formado, por alguna variable temporal inventada t

Esto implica que ninguna expresión se puede convertir implícitamente a void , ya que void t=e es ilegal para todas las expresiones e . Esto es cierto incluso si e es una expresión de tipo void , como void(3) .

Por lo tanto, una expresión de tipo void no se puede convertir implícitamente a void .

Lo que nos lleva a:

20.9.2 / 2 Requisitos [func.require]

Defina INVOKE (f, t1, t2, ..., tN, R) como INVOKE (f, t1, t2, ..., tN) convertido implícitamente a R.

En resumen, INVOKE(f, t1, t2, ..., tN, R) nunca es válido cuando R es void , ya que nada (incluido void ) puede convertirse implícitamente a void .

Como resultado de esto, todos los std::function<void(Args...)> tienen la propiedad !*this y, por lo tanto, no se pueden llamar, ya que los únicos constructores que no tienen !*this como una condición posterior (o no) copiar dicho estado desde otra function del mismo tipo) requiere Callable de uno de los parámetros.

20.9.11.2/7 Función de plantilla de clase [func.wrap.func]

Requiere: F será CopyConstructible. f será Callable (20.9.11.2) para los tipos de argumento ArgTypes y retorno tipo R. El constructor de copia y el destructor de A no lanzará excepciones.

20.9.11.2/2 Función de plantilla de clase [func.wrap.func]

Un objeto llamable f de tipo F es Callable para los tipos de argumento ArgTypes y devuelve el tipo R si la expresión INVOKE (f, declval () ..., R), considerada como un operando sin evaluar (Cláusula 5), ​​está bien formada ( 20.9.2).

Como se demostró anteriormente, no hay expresiones Callable para std::function<void(Args...)> .

Si de alguna manera se encontrara tal std::function<void(Args...)> , el operator() invocación operator() estaría mal formado:

invocación [func.wrap.func.inv]

Efectos: INVOKE (f, std :: forward (args) ..., R) (20.9.2), donde f es el objeto objetivo (20.9.1) de * this.

como INVOKE(f, std::forward<ArgTypes>(args)..., void) está mal formado para todos los argumentos y f .

¿Esta línea de razonamiento suena?