Realmente no estoy de acuerdo con esa convención de nomenclatura. Para mí, los parámetros de la plantilla que representan un tipo completamente genérico tienen sentido como una simple letra; este es un tipo T
, realmente no me importa cuál. Pero si hay algún requisito sobre el tipo, tiene sentido producir un nombre que identifique cuál es el argumento de la plantilla: template <typename T, typename Allocator> struct container
- el primer tipo es genérico, cualquier T
encajará, pero el segundo es un asignador.
no es diferente a las funciones, en las que no desea que sus parámetros a ser llamado a
, b
... con el fin o la apariencia, sino más bien producen nombres significativos como parte de la documentación . Además, si estoy desarrollando una guía de estilo, consideraría la necesidad de exigir los tipos locales de los argumentos de la plantilla en el caso de las clases de plantilla junto con las afirmaciones estáticas sobre los requisitos para los tipos (siempre que sea posible). Es decir, al menos hasta que conceptos proporcionan esa funcionalidad.
Considero que el STL es un buen ejemplo de biblioteca y se puede ver que la generación de typedefs ayuda a leer el código (desde g ++ 4.2 STL):
Clases:
// from <vector> -> <bits/stl_vector.h>
template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
class vector : protected _Vector_base<_Tp, _Alloc>
{
// Concept requirements.
typedef typename _Alloc::value_type _Alloc_value_type; // _Alloc contains value_type internal type
__glibcxx_class_requires(_Tp, _SGIAssignableConcept) // _Tp is assignable
[...]
public:
typedef _Tp value_type; // from here on _Tp is not mentioned
typedef typename _Tp_alloc_type::reference reference; // check + typedef
[...]
iterator begin() {
// without typedef:
// __gnu_cxx::__normal_iterator<pointer, vector_type> begin() {
[...]
reference operator[](size_type __n)
// _Tp& operator[](size_type __n)
[...]
// from <memory>
template <typename _Tp>
class auto_ptr
{
[...]
template <typename _Tp1>
auto_ptr(auto_ptr<_Tp1>& __a) throw()
y funciones:
template <typename _InputIterator, typename _Tp>
inline _InputIterator find(_InputIterator __first, _InputIterator __last, _Tp value) {
// concept requirements
__glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
[...]
todos sus nombres llevan el prefijo _
y, o bien una letra mayúscula o una segunda _
, que son los nombres reservados para la implementación, y prefieren _Tp
sobre _T
, pero al final se puede ver que cada vez que un tipo es genérico se llama _Tp
, _Tp1
..., mientras que cuando tiene algunos requisitos específicos adjuntos, se elige un nombre más sensato.
Hay una fina línea entre ellos, como por ejemplo en std::vector
, existen requisitos reales en el tipo genérico: _Tp
debe poder asignar, pero al final del día, es un tipo genérico mayoría. Llamarlo _Assignable
sería extraño en algunos contenedores donde hay más de un requisito.
Como escribió '_' o' __ * 'están reservados para la implementación (en ciertos ámbitos), en mi humilde opinión, es mejor evitar el uso de cualquier identificador así nombrado (aunque probablemente no hará ningún daño en práctica). –