Я не совсем согласен с этим соглашением об именах. Для меня параметры шаблона, представляющие полностью универсальный тип, имеют смысл в виде простой буквы - это тип T
, мне все равно, какой именно. Но если есть какие-либо требования к типу, имеет смысл создать имя, которое идентифицирует аргумент шаблона: template <typename T, typename Allocator> struct container
- первый тип является универсальным, подойдет любой T
, а второй - распределитель .
Он ничем не отличается от функций, в которых вы не хотите, чтобы ваши параметры назывались a
, b
... по порядку или виду, а вместо этого выдают значимые имена как часть документации . Кроме того, при разработке руководства по стилю я хотел бы рассмотреть вопрос о необходимости использования локальных определений типов аргументов шаблона в случае классов шаблонов вместе со статическими утверждениями о требованиях к типам (когда это возможно). То есть, по крайней мере, пока концепции не обеспечат эту функциональность.
Я считаю STL хорошим примером библиотеки, и вы можете видеть, что генерация typedefs помогает в чтении кода (из g ++ 4.2 stl):
Классы:
// 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()
А функции:
template <typename _InputIterator, typename _Tp>
inline _InputIterator find( _InputIterator __first, _InputIterator __last, _Tp value ) {
// concept requirements
__glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
[...]
Все их имена имеют префикс _
и либо заглавную букву, либо секунду _
, которые являются именами, зарезервированными для реализации, и они предпочитают _Tp
над _T
, но в конце вы можно видеть, что всякий раз, когда тип является универсальным , он называется _Tp
, _Tp1
..., тогда как когда к нему прикреплены некоторые конкретные требования, выбирается более разумное имя.
Между ними есть тонкая грань, как, например, в std::vector
, существуют фактические требования к универсальному типу: _Tp
должен быть назначаемым, но в конце дня это в основном универсальный тип. Называть его _Assignable
было бы странно в некоторых контейнерах, где существует более одного требования.