c++ - smart - ¿Por qué std:: result_of toma un tipo de función(no relacionado) como argumento de tipo?
smart contracts ethereum (4)
Creo que es solo que alguien tuvo la idea de que podría (ab) usar la notación de tipo de función para imitar la forma en que se vería la llamada al functor respectiva, y se quedó atascada. Entonces, no hay razones técnicas, solo estéticas.
// the result type of a call to (an object of) type F,
// passing (objects of) types A, B, and C as parameters.
result_of<F(A, B, C)>::type
Me doy cuenta de que las preguntas "por qué son las cosas como son" generalmente no son las mejores, pero hay muchas personas en SO que están sintonizadas con las discusiones estándar de los comités, así que espero que esto pueda responderse de manera efectiva, ya que tengo una curiosidad legítima sobre la respuesta es.
Básicamente, me tomó mucho tiempo descubrir qué estaba pasando con la firma de la plantilla de std::result_of
la primera vez que la vi: pensé que era una construcción totalmente nueva para los parámetros de la plantilla que nunca había visto antes.
template< class F, class... ArgTypes >
class result_of<F(ArgTypes...)>;
Después de pensarlo un poco, me di cuenta de lo que era en realidad: F(ArgTypes...)
es un tipo de función, pero no es el tipo de función cuyo tipo de resultado se evalúa (eso es solo F
): es el tipo de una función tomando ArgTypes...
argumentos y regresando tipo F
¿No es esto ... extraño? Tipo de hackish? ¿Alguien sabe si el comité alguna vez discutió alguna alternativa, como, por ejemplo, lo siguiente ...
template< class F, class... ArgTypes >
class result_of<F, ArgTypes...>;
?
Supongo que es posible que haya situaciones en las que el segundo constructo no se pueda usar tan fácilmente como el primero, pero ¿cuáles?
No estoy tratando de juzgar esto, pero es solo que esto fue legítimamente confuso para mí la primera vez que lo vi, así que tengo curiosidad de saber si hay una buena razón para ello. Me doy cuenta de que parte de la respuesta podría ser simplemente "porque Boost lo hizo" de esa manera, pero aún así dejan las preguntas restantes (objetivas) ...
¿Existe alguna razón técnica por la que Boost elija esta sintaxis para codificar información de tipo en lugar de otra alternativa?
¿Hubo alguna discusión por parte del comité de C ++ 11 sobre qué tan apropiado era estandarizar esto, dado que
std::result_of
puede ser implementado en términos dedecltype
bastante fácil de todos modos?
result_of
era parte de TR1, que salió antes de que se agregara decltype al idioma. Pero fue diseñado decltype
en cuenta el decltype
de decltype
, por lo que cambiar la implementación de result_of
para usar decltype
es simple. Sí, es un truco, pero funciona.
Tener un tipo de función como parámetro te permite tener una plantilla de clase "variadic" sin restricciones incluso en C ++ 03. Piénselo: en C ++ 03, no teníamos plantillas variadas. Y no se puede "sobrecargar" una plantilla de clase como se puede con las plantillas de funciones, entonces, ¿cómo sería posible permitir diferentes cantidades de "argumentos" para la función?
Utilizando un tipo de función, puede agregar cualquier número parcial de especializaciones para la diferente cantidad de parámetros:
template<class Fty>
struct result_of;
template<class F>
struct result_of<F()>{ /*...*/ };
template<class F, class A0>
struct result_of<F(A0)>{ /*...*/ };
template<class F, class A0, class A1>
struct result_of<F(A0, A1)>{ /*...*/ };
// ...
La única otra forma de hacerlo en C ++ 03 son los argumentos de plantilla predeterminados y parcialmente especializados para cada caso, la desventaja es que ya no se parece a una llamada de función, y que cualquier tipo de contenedor que use result_of
internamente puede '' Solo paso Sig
.
Ahora, hay una desventaja con el modo de función: también obtiene todas las transformaciones habituales realizadas a los "parámetros": R(Args...)
-> R(*)(Args...)
y lo más importante T[N]
-> T*
y cv-qualifiers de alto nivel descartados ( §8.3.5/5
):
struct X{
bool operator()(int (&&arr)[3]);
long operator()(void*);
};
static_assert(std::is_same<std::result_of<X(int[3])>::type, bool>(), "/cry");
Ejemplo en vivo Salida:
error: la aserción estática falló: / cry
El otro problema es que los cv-qualifiers de nivel superior se descartan:
struct Y{};
struct X{
bool operator()(Y const&);
long operator()(Y&&);
};
Y const f();
static_assert(std::is_same<std::result_of<X(Y const)>::type, bool>(), "/cry");
Ejemplo en vivo Salida:
error: la aserción estática falló: / cry
(Esto amplía la respuesta de JohannesD y el comentario de Jesse Good al respecto, pero esto no cabe en un comentario. Voto por favor esa otra respuesta, no esta).
De N1454 Sintaxis y ejemplos :
La definición del comportamiento de
result_of
es directa: dados los tiposF
,T1
,T2
, ...,TN
y lvaluesf
,t1
,t2
, ...,tN
de esos tipos, respectivamente, el tipo expresión
result_of<F(T1, T2, ..., TN)>::type
evalúa el tipo de expresión
f(t1, t2, ..., tN)
.
¡Esto no está abusando del sistema de tipos, es hermosamente elegante!