¿Por qué no se requiere usar typename para tipos dependientes en el siguiente caso?

10

He estado leyendo sobre eliminar la referencia de un tipo, aquí .

Da el siguiente ejemplo:

#include <iostream> // std::cout
#include <type_traits> // std::is_same

template<class T1, class T2>
void print_is_same() {
  std::cout << std::is_same<T1, T2>() << '\n';
}

int main() {
  std::cout << std::boolalpha;

  print_is_same<int, int>();
  print_is_same<int, int &>();
  print_is_same<int, int &&>();

  print_is_same<int, std::remove_reference<int>::type>(); // Why not typename std::remove_reference<int>::type ?
  print_is_same<int, std::remove_reference<int &>::type>();// Why not typename std::remove_reference<int &>::type ?
  print_is_same<int, std::remove_reference<int &&>::type>();// Why not typename std::remove_reference<int &&>::type ?
}

Los types en los std::remove_referencerasgos son tipos dependientes.

Posible implementación

template< class T > struct remove_reference      {typedef T type;};
template< class T > struct remove_reference<T&>  {typedef T type;};
template< class T > struct remove_reference<T&&> {typedef T type;};

¿Pero por qué no se usa typename std::remove_reference</*TYPE*/>::type?

LernerCpp
fuente

Respuestas:

22

Los types en los std::remove_referencerasgos son tipos dependientes.

No, no son nombres dependientes aquí. Los argumentos de la plantilla se han especificado explícitamente como int, int&y int&&. Por lo tanto, los tipos son conocidos en este punto.

Por otro lado, si lo usa std::remove_referencecon un parámetro de plantilla, p. Ej.

template <typename T>
void foo() {
    print_is_same<int, typename std::remove_reference<T>::type>();
}

entonces tiene que usar typenamepara decir que std::remove_reference<T>::typees un tipo ya que su expresión ahora depende del parámetro de la plantilla T.

songyuanyao
fuente
5

En pocas palabras, debe typenameasegurarse de que el compilador

std::remove_reference<int>::type

Realmente es un tipo. Consideremos alguna otra plantilla

template <typename T>
struct foo {
    using type = int;
};

Aquí foo::typehay un tipo. Pero, ¿qué pasa si alguien proporciona una especialización en la línea de

template <> struct foo<int> {
    int type;
};

Ahora typeno es un tipo sino un int. Ahora cuando usas foo dentro de una plantilla:

template <typanem T> 
struct bar {
    using type = typename foo<T>::type;
};

Debe asegurarse de que el compilador sea foo<T>::typerealmente un tipo, no otra cosa, porque solo mirando bar(y la plantilla principal foo) el compilador no puede saberlo.

Sin embargo, en su mainel std::remove_reference<int>::typeparámetro no depende de la plantilla, por lo tanto, el compilador puede comprobar fácilmente si se trata de un tipo.

idclev 463035818
fuente
0

La palabra clave typename se usa para ayudar al compilador a analizar la fuente. Señala que la identificación es un nombre de tipo, no un nombre de variable o nombre de método. Pero en situaciones como el compilador anterior puede resolverlo por sí mismo, por lo que esta palabra clave no es necesaria.

Sergey Strukov
fuente