c++ - result_of para el objeto miembro con argumento cv calificado
c++14 language-lawyer (2)
En result_of_t<Mp(const MyClass)>
parece que intenta preguntar cuál es el tipo de resultado de invocar Mp
con un valor const
de tipo MyClass
. Una mejor manera de preguntar eso con result_of
sería result_of_t<Mp(const MyClass&&)>
pero generalmente es más fácil usar decltype
y olvidar que result_of
alguna vez existió. Si realmente pretendía preguntar el resultado con un valor const
entonces sería result_of_t<Mp(const MyClass&)>
.
Es cierto que la const
nivel superior en los parámetros de la función no tiene ningún significado en una declaración de función. result_of
tanto, cuando se utiliza result_of
, tiene más sentido proporcionar tipos de argumentos como referencias a tipos posiblemente const
. Esto también hace que la categoría de valor sea explícita, sin pérdida de expresividad. Podemos usar el truco print_type
para ver qué sucede cuando hacemos esto:
template <typename...> struct print_type; // forward declaration
print_type<std::result_of_t<Mp(const MyClass)>,
std::result_of_t<Mp(const MyClass&)>,
std::result_of_t<Mp(const MyClass&&)>,
std::result_of_t<Mp(MyClass)>,
std::result_of_t<Mp(MyClass&)>,
std::result_of_t<Mp(MyClass&&)>>{};
Esto imprime:
error: invalid use of incomplete type ''struct print_type<int&&, const int&, const int&&, int&&, int&, int&&>''
Así podemos deducir:
std::result_of_t<Mp(const MyClass)> == int&&
std::result_of_t<Mp(const MyClass&)> == const int&
std::result_of_t<Mp(const MyClass&&)> == const int&&
std::result_of_t<Mp(MyClass)> == int&&
std::result_of_t<Mp(MyClass&)> == int&
std::result_of_t<Mp(MyClass&&)> == int&&
Podemos ver que result_of_t<Mp(const MyClass)>
, result_of_t<Mp(MyClass)>
, y result_of_t<Mp(MyClass&&)>
significan lo mismo. Me sorprendería si no lo hicieran.
Tenga en cuenta que cuando utiliza declval
, también proporciona tipos de argumentos como referencias, ya que declval
se declara para devolver una referencia. Además, todos los parámetros a std::invoke
son referencias.
Dadas las siguientes declaraciones:
struct MyClass { };
typedef int MyClass::*Mp;
Tanto en gcc 6.2 como en el compilador Clang que he probado, result_of<Mp(const MyClass)>::type
produce int&&
.
Resumen de mi pregunta: ¿Por qué int&&
y no const int&&
o & const int&&
o simplemente int
?
Más antecedentes: el estándar dice que result_of
se define de esta manera:
el tipo typedef miembro debe nombrar el tipo
decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...));
El estándar también define INVOKE para objetos puntero a miembro de esta manera:
- t1. * F cuando N == 1 y f es un puntero al miembro de datos de una clase T y
is_base_of_v<T, decay_t<decltype(t1)>>
es verdadero;
Tenga en cuenta que decay_t
es solo para comprobar si se aplica este punto. Por lo que puedo decir, la aplicación de los dos puntos anteriores debe producir:
decltype(declval<const MyClass>().*declval<Mp>())
Lo que produce const int&&
. Entonces, ¿me estoy perdiendo algo, o están equivocadas las bibliotecas del compilador?
Edición, 30 de agosto de 2016:
Gracias por las respuestas. Varias personas han sugerido formas alternativas de obtener el resultado correcto sin usar result_of
. Debería aclarar que la razón por la que estoy colgado de la definición correcta de result_of
es que en realidad estoy implementando la implementación más razonable y razonable de result_of
que funciona con un compilador anterior a C ++ 11. Entonces, aunque estoy de acuerdo en que puedo usar decltype
o result_of<Mp(const MyClass&&)>::type
en C ++ 11, no hacen lo que necesito para C ++ 03. Varias personas han dado la respuesta correcta, que es que los argumentos const rvalue para las funciones no son parte del tipo de función. Esto aclara las cosas para mí e implementaré mi resultado anterior a C ++ 11 de manera que también descarte esos calificadores.
const
es despojado de los parámetros de la función. Puedes verificar esto usando is_same
.
void(int) == void(const int)
Mp(MyClass) == Mp(const MyClass)
result_of<Mp(MyClass)> == result_of<Mp(const MyClass)>
Creo que esto se explica por [8.3.5.5]
:
Después de generar la lista de tipos de parámetros, todos los calificadores cv de nivel superior que modifican un tipo de parámetro se eliminan al formar el tipo de función. La lista resultante de tipos de parámetros transformados y la presencia o ausencia de puntos suspensivos o un paquete de parámetros de funciones es la lista de tipos de parámetros de la función. [Nota: esta transformación no afecta los tipos de los parámetros. Por ejemplo,
int(*)(const int p, decltype(p)*)
eint(*)(int, const int*)
son tipos idénticos. - nota final]
Puede result_of
definiendo su propio result_of
que no (mal) use tipos de funciones:
template <typename F, typename... ArgTypes>
struct my_result_of
{
using type = decltype(std::invoke(std::declval<F>(), std::declval<ArgTypes>()...));
};
Esta definición es realmente lo que el estándar debería haber usado.