Шаблоны C++

Поделись знанием:
Перейти к: навигация, поиск

Шабло́ны (англ. template) — средство языка C++, предназначенное для кодирования обобщённых алгоритмов, без привязки к некоторым параметрам (например, типам данных, размерам буферов, значениям по умолчанию).

В C++ возможно создание шаблонов функций и классов.

Шаблоны позволяют создавать параметризованные классы и функции. Параметром может быть любой тип или значение одного из допустимых типов (целое число, enum, указатель на любой объект с глобально доступным именем, ссылка). Например, нам нужен какой-то класс:

class SomeClass{
	int SomeValue;
	int SomeArray[20];
	...
};

Для одной конкретной цели мы можем использовать этот класс. Но, вдруг, цель немного изменилась, и нужен еще один класс. Теперь нужно 30 элементов массива SomeArray и вещественный тип SomeValue элементов SomeArray. Тогда мы можем абстрагироваться от конкретных типов и использовать шаблоны с параметрами. Синтаксис: в начале перед объявлением класса напишем слово template и укажем параметры в угловых скобках. В нашем примере:

template < int ArrayLength, typename SomeValueType > class SomeClass{
	SomeValueType SomeValue;
	SomeValueType SomeArray[ ArrayLength ];
	...
};

Тогда для первой модели пишем:

SomeClass < 20, int > SomeVariable;

для второй:

SomeClass < 30, double > SomeVariable2;

Хотя шаблоны предоставляют краткую форму записи участка кода, на самом деле их использование не сокращает исполняемый код, так как для каждого набора параметров компилятор создаёт отдельный экземпляр функции или класса. Как следствие, исчезает возможность совместного использования скомпилированного кода в рамках разделяемых библиотек.





Шаблоны функций

Синтаксис описания шаблона

Шаблон функции начинается с ключевого слова template, за которым в угловых скобках следует список параметров. Затем следует объявление функции:

template< typename T > 
void sort( T array[], int size );  // прототип: шаблон sort объявлен, но не определён 

template< typename T >
void sort( T array[], int size )   // объявление и определение
{
  T t;
  for (int i = 0; i < size - 1; i++)
  {
    for (int j = size - 1; j > i; j--)
    {
      if (array[j] < array[j-1])
      {
        t = array[j];
        array[j] = array[j-1];
        array[j-1] = t;
      }
    }
  } 
}

template< int BufferSize >         // целочисленный параметр
char* read()
{
  char *Buffer = new char[ BufferSize ];
  /* считывание данных */
  return Buffer;
}

Ключевое слово typename появилось сравнительно недавно, поэтому стандарт[1] допускает использование class вместо typename:

template< class T >

Вместо T допустим любой другой идентификатор.

Пример использования

Простейшим примером служит определение минимума из двух величин.

Если a меньше b то вернуть а, иначе — вернуть b

В отсутствие шаблонов программисту приходится писать отдельные функции для каждого используемого типа данных. Хотя многие языки программирования определяют встроенную функцию минимума для элементарных типов (таких как целые и вещественные числа), такая функция может понадобиться и для сложных (например «время» или «строка») и очень сложных («игрок» в онлайн-игре) объектов.

Так выглядит шаблон функции определения минимума:

template< typename T >
T min( T a, T b )
{
  return a < b ? a : b;
}

Для вызова этой функции можно просто использовать её имя:

min( 1, 2 );
min( 'a', 'b' );
min( string( "abc" ), string( "cde" ) );

Вызов шаблонной функции

Вообще говоря, для вызова шаблонной функции, необходимо указать значения для всех параметров шаблона. Для этого после имени шаблона указывается список значений в угловых скобках:

int i[5] = { 5, 4, 3, 2, 1 };
sort< int >( i, 5 );

char c[] = "бвгда";
sort< char >( c, strlen( c ) );

sort< int >( c, 5 );    // ошибка: у sort< int > параметр int[], а не char[]

char *ReadString = read< 20 >();
delete [] ReadString;
ReadString = read< 30 >();

Для каждого набора параметров компилятор генерирует новый экземпляр функции. Процесс создания нового экземпляра называется инстанцированием шаблона.

В примере выше компилятор создал две специализации шаблона функции sort (для типов char и int) и две специализации шаблона read (для значений BufferSize 20 и 30). Последнее скорее всего расточительно, так как для каждого возможного значения параметра компилятор будет создавать новые и новые экземпляры функций, которые будут отличаться лишь одной константой.

Выведение значений параметров

В некоторых случаях компилятор может сам вывести (логически определить) значение параметра шаблона функции из аргумента функции. Например, при вызове вышеописанной функции sort необязательно указывать параметр шаблона (если он совпадает с типом элементов аргумента-массива):

int i[5] = { 5, 4, 3, 2, 1 };
sort( i, 5 );                   // вызывается sort< int >

char c[] = "бвгда";
sort( c, strlen( c ) );         // вызывается sort< char >

Возможно выведение и в более сложных случаях.

В случае использования шаблонов классов с целыми параметрами также возможно выведение этих параметров. Например:

template< int size >
class IntegerArray
{
  int Array[ size ];
  /* ... */
};
template< int size >  // Прототип шаблона
void PrintArray( IntegerArray< size > array ) { /* ... */ } // Вызов шаблона

// Использование объекта шаблона
IntegerArray<20> ia;
PrintArray( ia );

Правила выведения введены в язык для облегчения использования шаблона и для избежания возможных ошибок, например попытка использования sort< int > для сортировки массива символов.

Если параметр шаблона можно вывести по нескольким аргументам, то результат выведения должен быть в точности одинаков для всех этих аргументов. Например, следующие вызовы ошибочны:

min (0, 'a');
min (7, 7.0);

Ошибки в шаблонах

Некоторые ошибки в описании шаблона могут быть выявлены уже в месте описания. Эти ошибки не зависят от конкретных параметров. Например:

template< class T >
void f( T data )
{
  T *pt = 7;                       // ошибка: инициализация указателя целым числом
  datA = 0;                        // ошибка: неизвестный идентификатор datA
  *pt = data                       // ошибка: нет точки с запятой
}

Ошибки, связанные с использованием конкретных параметров шаблона, нельзя выявить до того, как шаблон использован. Например, шаблон min сам по себе не содержит ошибок, однако использование его с типами, для которых операция '<' не определена, приведёт к ошибке:

struct A
{
  int a;
};
A obj1, obj2;
min( obj1, obj2 );

Если ввести операцию '<' до первого использования шаблона, то ошибка будет устранена. Так проявляется гибкость шаблонов в C++:

friend inline bool operator< ( const A& a1, const A& a2 ) { return a1.a < a2.a; }

min( obj1, obj2 );

Шаблоны классов

В классе, реализующем связный список целых чисел, алгоритмы добавления нового элемента списка, поиска нужного элемента не зависят от того, что элементы списка — целые числа. Те же алгоритмы применялись бы и для списка символов, строк, дат, классов игроков, и так далее.

template< class T >
class List
{
  /* ... */
public:
  void Add( const T& Element );
  bool Find( const T& Element );
  /* ... */
};

Использование шаблонов

Для использования шаблона класса, необходимо указать его параметры:

List<int> li;
List<string> ls;
li.Add( 17 );
ls.Add( "Hello!" );

Технические подробности

Параметры шаблонов

Параметрами шаблонов могут быть: параметры-типы, параметры обычных типов, параметры-шаблоны.

Для параметров любого типа можно указывать значения по умолчанию.

template< class T1,                    // параметр-тип
          typename T2,                 // параметр-тип
          int I,                       // параметр обычного типа
          T1 DefaultValue,             // параметр обычного типа
          template< class > class T3,  // параметр-шаблон
          class Character = char       // параметр по умолчанию
        >

Параметры-шаблоны

Если в шаблоне класса или функции необходимо использовать один и тот же шаблон, но с разными параметрами, то используются параметры-шаблоны. Например:

  template< class Type, template< class > class Container >
  class CrossReferences
  {
    Container< Type > mems;
    Container< Type* > refs;
    /* ... */
  };

  CrossReferences< Date, vector > cr1;
  CrossReferences< string, set > cr2;

Нельзя использовать шаблоны функций в качестве параметров-шаблонов.

Правила выведения аргументов шаблона функции

Для параметров, которые являются типами (например параметр T функции sort) возможно выведение, если аргумент функции имеет один из следующих типов:

Тип аргумента Описание
T
const T
volatile T
Сам тип T, возможно с модификаторами const или volatile.
  template< class T >
  T ReturnMe( const T arg ) { return arg; }

  ReturnMe( 7 );
  ReturnMe( 'a' );
T*
T&
T[A]

A — константа
Указатель, ссылка или массив элементов типа T.

Примером может служить шаблон функции sort, рассмотренный выше

Templ<T>
Templ — имя шаблона класса
В качестве аргумента, функция требует конкретную специализацию некоторого шаблона.
  #include <vector>

  template< class T >
  void sort( vector< T > array ) { /* сортировка */ }

  vector<int> i;
  vector<char> c;
  sort( i );
  sort( c );
T (*) (args)
args — некие аргументы
Указатель на функцию, которая возвращает тип T.
  template< class T >
  T* CreateArray( T(*GetValue)(), const int size )
  {
    T *Array = new T[ size ];
    for( int i = 0; i < size; i++ )
      Array[i] = GetValue();
    return Array;
  }

  int GetZero() { return 0; }
  char InputChar()
  {
    char c;
    cin >> c;
    return c;
  }

  int *ArrayOfZeros = CreateArray( GetZero, 20 );
  char *String = CreateArray( InputChar, 40 );
type T::*
T Class::*

type — некий тип
Class — некий класс
Указатель на член класса T произвольного типа.
Указатель на член типа T произвольного класса.
  class MyClass
  {
  public:
    int a;
  };

  template< class T >
  T& IncrementIntegerElement( int T::* Element, T& Object )
  {
    Object.*Element += 1;
    return Object;
  }

  template< class T >
  T IncrementMyClassElement( T MyClass::* Element, MyClass& Object )
  {
    Object.*Element += 1;
    return Object.*Element;
  }

  MyClass Obj;
  int n;

  n = ( IncrementIntegerElement( &MyClass::a, Obj ) ).a;
  n = IncrementMyClassElement( &MyClass::a, Obj );
type (T::*) (args)
T (Class::*) (args)

type — некий тип
Class — некий класс
args — некие аргументы
Указатель на функцию-член класса T произвольного типа.
Указатель на функцию-член типа T произвольного класса.
  class MyClass
  {
  public:
    int a;
    int IncrementA();
  };
  int MyClass::IncrementA() { return ++a; }

  template< class T >
  T& CallIntFunction( int (T::* Function)(), T& Object )
  {
    (Object.*Function)();
    return Object;
  }

  template< class T >
  T CallMyClassFunction( T (MyClass::* Function)(), MyClass& Object )
  {
    return (Object.*Function)();
  }

  MyClass Obj;
  int n;

  n = ( CallIntFunction( &MyClass::IncrementA, Obj ) ).a;
  n = CallMyClassFunction( &MyClass::IncrementA, Obj );

Члены классов-шаблонов

Члены шаблона класса являются шаблонами, причём с той же, что и у шаблона класса, параметризацией. В частности это означает, что определение функций-членов следует начинать с заголовка шаблона:

  template< class T >
  class A
  {
    void f( T data );
    void g( void );
  public:
    A();
  };

  template< class T >
  void A<T>::f( T data );

  template< class T >
  void A<T>::g( void );

Внутри области видимости шаблона не нужно повторять спецификатор. Это значит, что например A<T>::A() — это конструктор, хотя можно писать и A<T>::A<T>().

Типы как члены классов

Если параметром шаблона является класс, у которого есть член, являющийся типом данных, то для использования этого члена, нужно применять ключевое слово typename. Например:

  class Container
  {
  public:
    int array[ 15 ];
    typedef int* iterator;
    /* ... */
    iterator begin() { return array; }
  };

  template< class C >
  void f( C& vector )
  {
    C::iterator i = vector.begin();          // ошибка
    typename C::iterator i = vector.begin();
  }

Шаблоны как члены классов

Проблемы возникают и с членами-шаблонами. Если шаблон (ConvertTo()), который является членом класса (A), который в свою очередь является параметром шаблона (f), используется в этом шаблоне (f) и не допускает выведения параметров, то необходимо использовать квалификатор template:

  class A
  {
  /* ... */
  public:
    template< class T > T& ConvertTo();
    template< class T > void ConvertFrom( const T& data );
  };

  template< class T >
  void f( T Container )
  {
    int i1 = Container.template ConvertTo<int>() + 1;
    Container.ConvertFrom( i1 );                          // квалификатор не нужен
  }

Критика и сравнение с альтернативами

Шаблонное метапрограммирование в С++ страдает от множества ограничений, включая проблемы портируемости, отсутствие поддержки отладки или ввода/вывода в процессе инстанцирования шаблонов, длительное время компиляции, низкую читабельность кода, скудную диагностику ошибок и малопонятные сообщения об ошибках[2]. Подсистема шаблонов С++ определяется как полный по Тьюрингу чистый функциональный язык программирования, но программисты в функциональном стиле считают это провокацией и не спешат признавать С++ успешным языком[3].

Многие языки (Java 5, Ада, Delphi 2009) реализуют поддержку обобщённого программирования более простым способом, некоторые даже на уровне системы типов (см. Eiffel, а также параметрический полиморфизм в языках семейства ML); такие языки не нуждаются в механизмах, похожих на шаблоны С++.

Средства макроподстановки в Си, хоть и не обладают Тьюринг-полнотой, представляют собой достаточное для низкоуровневого программирования средство порождающего программирования, и в C99 их возможности были существенно расширены.

Язык D обладает шаблонами, более мощными, чем C++.[4].

См. также

Напишите отзыв о статье "Шаблоны C++"

Примечания

  1. Стандарт C++ «Standard for the C++ Programming Language»: [mrst.narod.ru/c-cpp/ ISO/IEC 14882 1998].
  2. K. Czarnecki, J. O’Donnell, J. Striegnitz, W. Taha [camlunity.ru/swap/Library/Computer%20Science/Metaprogramming/Domain-Specific%20Languages/DSL%20Implementation%20in%20MetaOCaml,%20Template%20Haskell%20and%20C++.pdf DSL implementation in metaocaml, template haskell, and C++]. — University of Waterloo, University of Glasgow, Research Centre Julich, Rice University, 2004..
    Цитата: C++ Template Metaprogramming suffers from a number of limitations, including portability problems due to compiler limitations (although this has significantly improved in the last few years), lack of debugging support or IO during template instantiation, long compilation times, long and incomprehensible errors, poor readability of the code, and poor error reporting.
  3. Sheard T., Jones S.P. [research.microsoft.com/en-us/um/people/simonpj/papers/meta-haskell/ Template Metaprogramming for Haskell] // Haskell Workshop. — Pittsburgh: ACM 1-58113-415-0/01/0009, 2002..
    Цитата: Robinson’s provocative paper identifies C++ templates as a major, albeit accidental, success of the C++ language design. Despite the extremely baroque nature of template meta-programming, templates are used in fascinating ways that extend beyond the wildest dreams of the language designers. Perhaps surprisingly, in view of the fact that templates are functional programs, functional programmers have been slow to capitalize on C++’s success
  4. [www.digitalmars.com/d/2.0/template-comparison.html Digital Mars: D Programming Language 2.0]  (англ.)

Литература

  • Дэвид Вандевурд, Николай М. Джосаттис. Шаблоны C++: справочник разработчика = C++ Templates: The Complete Guide. — М.: «Вильямс», 2003. — С. 544. — ISBN 0-201-73484-2.
  • Подбельский В. В. 6.9. Шаблоны функций //Глава 6. Функции, указатели, ссылки // Язык Си++ / рец. Дадаев Ю. Г.. — 4. — М.: Финансы и статистика, 2003. — С. 230-236. — 560 с. — ISBN 5-279-02204-7, УДК 004.438Си(075.8) ББК 32.973.26-018 1я173.

Ссылки

  • [www.realcoding.net/article/view/4265 Использование шаблонов в C++]

Отрывок, характеризующий Шаблоны C++

Только что князь Андрей отъехал, он остановил его.
– Et demandez lui, si les tirailleurs sont postes, – прибавил он. – Ce qu'ils font, ce qu'ils font! [И спросите, размещены ли стрелки. – Что они делают, что они делают!] – проговорил он про себя, все не отвечая австрийцу.
Князь Андрей поскакал исполнять поручение.
Обогнав всё шедшие впереди батальоны, он остановил 3 ю дивизию и убедился, что, действительно, впереди наших колонн не было стрелковой цепи. Полковой командир бывшего впереди полка был очень удивлен переданным ему от главнокомандующего приказанием рассыпать стрелков. Полковой командир стоял тут в полной уверенности, что впереди его есть еще войска, и что неприятель не может быть ближе 10 ти верст. Действительно, впереди ничего не было видно, кроме пустынной местности, склоняющейся вперед и застланной густым туманом. Приказав от имени главнокомандующего исполнить упущенное, князь Андрей поскакал назад. Кутузов стоял всё на том же месте и, старчески опустившись на седле своим тучным телом, тяжело зевал, закрывши глаза. Войска уже не двигались, а стояли ружья к ноге.
– Хорошо, хорошо, – сказал он князю Андрею и обратился к генералу, который с часами в руках говорил, что пора бы двигаться, так как все колонны с левого фланга уже спустились.
– Еще успеем, ваше превосходительство, – сквозь зевоту проговорил Кутузов. – Успеем! – повторил он.
В это время позади Кутузова послышались вдали звуки здоровающихся полков, и голоса эти стали быстро приближаться по всему протяжению растянувшейся линии наступавших русских колонн. Видно было, что тот, с кем здоровались, ехал скоро. Когда закричали солдаты того полка, перед которым стоял Кутузов, он отъехал несколько в сторону и сморщившись оглянулся. По дороге из Працена скакал как бы эскадрон разноцветных всадников. Два из них крупным галопом скакали рядом впереди остальных. Один был в черном мундире с белым султаном на рыжей энглизированной лошади, другой в белом мундире на вороной лошади. Это были два императора со свитой. Кутузов, с аффектацией служаки, находящегося во фронте, скомандовал «смирно» стоявшим войскам и, салютуя, подъехал к императору. Вся его фигура и манера вдруг изменились. Он принял вид подначальственного, нерассуждающего человека. Он с аффектацией почтительности, которая, очевидно, неприятно поразила императора Александра, подъехал и салютовал ему.
Неприятное впечатление, только как остатки тумана на ясном небе, пробежало по молодому и счастливому лицу императора и исчезло. Он был, после нездоровья, несколько худее в этот день, чем на ольмюцком поле, где его в первый раз за границей видел Болконский; но то же обворожительное соединение величавости и кротости было в его прекрасных, серых глазах, и на тонких губах та же возможность разнообразных выражений и преобладающее выражение благодушной, невинной молодости.
На ольмюцком смотру он был величавее, здесь он был веселее и энергичнее. Он несколько разрумянился, прогалопировав эти три версты, и, остановив лошадь, отдохновенно вздохнул и оглянулся на такие же молодые, такие же оживленные, как и его, лица своей свиты. Чарторижский и Новосильцев, и князь Болконский, и Строганов, и другие, все богато одетые, веселые, молодые люди, на прекрасных, выхоленных, свежих, только что слегка вспотевших лошадях, переговариваясь и улыбаясь, остановились позади государя. Император Франц, румяный длиннолицый молодой человек, чрезвычайно прямо сидел на красивом вороном жеребце и озабоченно и неторопливо оглядывался вокруг себя. Он подозвал одного из своих белых адъютантов и спросил что то. «Верно, в котором часу они выехали», подумал князь Андрей, наблюдая своего старого знакомого, с улыбкой, которую он не мог удержать, вспоминая свою аудиенцию. В свите императоров были отобранные молодцы ординарцы, русские и австрийские, гвардейских и армейских полков. Между ними велись берейторами в расшитых попонах красивые запасные царские лошади.
Как будто через растворенное окно вдруг пахнуло свежим полевым воздухом в душную комнату, так пахнуло на невеселый Кутузовский штаб молодостью, энергией и уверенностью в успехе от этой прискакавшей блестящей молодежи.
– Что ж вы не начинаете, Михаил Ларионович? – поспешно обратился император Александр к Кутузову, в то же время учтиво взглянув на императора Франца.
– Я поджидаю, ваше величество, – отвечал Кутузов, почтительно наклоняясь вперед.
Император пригнул ухо, слегка нахмурясь и показывая, что он не расслышал.
– Поджидаю, ваше величество, – повторил Кутузов (князь Андрей заметил, что у Кутузова неестественно дрогнула верхняя губа, в то время как он говорил это поджидаю ). – Не все колонны еще собрались, ваше величество.
Государь расслышал, но ответ этот, видимо, не понравился ему; он пожал сутуловатыми плечами, взглянул на Новосильцева, стоявшего подле, как будто взглядом этим жалуясь на Кутузова.
– Ведь мы не на Царицыном лугу, Михаил Ларионович, где не начинают парада, пока не придут все полки, – сказал государь, снова взглянув в глаза императору Францу, как бы приглашая его, если не принять участие, то прислушаться к тому, что он говорит; но император Франц, продолжая оглядываться, не слушал.
– Потому и не начинаю, государь, – сказал звучным голосом Кутузов, как бы предупреждая возможность не быть расслышанным, и в лице его еще раз что то дрогнуло. – Потому и не начинаю, государь, что мы не на параде и не на Царицыном лугу, – выговорил он ясно и отчетливо.
В свите государя на всех лицах, мгновенно переглянувшихся друг с другом, выразился ропот и упрек. «Как он ни стар, он не должен бы, никак не должен бы говорить этак», выразили эти лица.
Государь пристально и внимательно посмотрел в глаза Кутузову, ожидая, не скажет ли он еще чего. Но Кутузов, с своей стороны, почтительно нагнув голову, тоже, казалось, ожидал. Молчание продолжалось около минуты.
– Впрочем, если прикажете, ваше величество, – сказал Кутузов, поднимая голову и снова изменяя тон на прежний тон тупого, нерассуждающего, но повинующегося генерала.
Он тронул лошадь и, подозвав к себе начальника колонны Милорадовича, передал ему приказание к наступлению.
Войско опять зашевелилось, и два батальона Новгородского полка и батальон Апшеронского полка тронулись вперед мимо государя.
В то время как проходил этот Апшеронский батальон, румяный Милорадович, без шинели, в мундире и орденах и со шляпой с огромным султаном, надетой набекрень и с поля, марш марш выскакал вперед и, молодецки салютуя, осадил лошадь перед государем.
– С Богом, генерал, – сказал ему государь.
– Ma foi, sire, nous ferons ce que qui sera dans notre possibilite, sire, [Право, ваше величество, мы сделаем, что будет нам возможно сделать, ваше величество,] – отвечал он весело, тем не менее вызывая насмешливую улыбку у господ свиты государя своим дурным французским выговором.
Милорадович круто повернул свою лошадь и стал несколько позади государя. Апшеронцы, возбуждаемые присутствием государя, молодецким, бойким шагом отбивая ногу, проходили мимо императоров и их свиты.
– Ребята! – крикнул громким, самоуверенным и веселым голосом Милорадович, видимо, до такой степени возбужденный звуками стрельбы, ожиданием сражения и видом молодцов апшеронцев, еще своих суворовских товарищей, бойко проходивших мимо императоров, что забыл о присутствии государя. – Ребята, вам не первую деревню брать! – крикнул он.
– Рады стараться! – прокричали солдаты.
Лошадь государя шарахнулась от неожиданного крика. Лошадь эта, носившая государя еще на смотрах в России, здесь, на Аустерлицком поле, несла своего седока, выдерживая его рассеянные удары левой ногой, настораживала уши от звуков выстрелов, точно так же, как она делала это на Марсовом поле, не понимая значения ни этих слышавшихся выстрелов, ни соседства вороного жеребца императора Франца, ни всего того, что говорил, думал, чувствовал в этот день тот, кто ехал на ней.
Государь с улыбкой обратился к одному из своих приближенных, указывая на молодцов апшеронцев, и что то сказал ему.


Кутузов, сопутствуемый своими адъютантами, поехал шагом за карабинерами.
Проехав с полверсты в хвосте колонны, он остановился у одинокого заброшенного дома (вероятно, бывшего трактира) подле разветвления двух дорог. Обе дороги спускались под гору, и по обеим шли войска.
Туман начинал расходиться, и неопределенно, верстах в двух расстояния, виднелись уже неприятельские войска на противоположных возвышенностях. Налево внизу стрельба становилась слышнее. Кутузов остановился, разговаривая с австрийским генералом. Князь Андрей, стоя несколько позади, вглядывался в них и, желая попросить зрительную трубу у адъютанта, обратился к нему.
– Посмотрите, посмотрите, – говорил этот адъютант, глядя не на дальнее войско, а вниз по горе перед собой. – Это французы!
Два генерала и адъютанты стали хвататься за трубу, вырывая ее один у другого. Все лица вдруг изменились, и на всех выразился ужас. Французов предполагали за две версты от нас, а они явились вдруг, неожиданно перед нами.
– Это неприятель?… Нет!… Да, смотрите, он… наверное… Что ж это? – послышались голоса.
Князь Андрей простым глазом увидал внизу направо поднимавшуюся навстречу апшеронцам густую колонну французов, не дальше пятисот шагов от того места, где стоял Кутузов.
«Вот она, наступила решительная минута! Дошло до меня дело», подумал князь Андрей, и ударив лошадь, подъехал к Кутузову. «Надо остановить апшеронцев, – закричал он, – ваше высокопревосходительство!» Но в тот же миг всё застлалось дымом, раздалась близкая стрельба, и наивно испуганный голос в двух шагах от князя Андрея закричал: «ну, братцы, шабаш!» И как будто голос этот был команда. По этому голосу всё бросилось бежать.
Смешанные, всё увеличивающиеся толпы бежали назад к тому месту, где пять минут тому назад войска проходили мимо императоров. Не только трудно было остановить эту толпу, но невозможно было самим не податься назад вместе с толпой.
Болконский только старался не отставать от нее и оглядывался, недоумевая и не в силах понять того, что делалось перед ним. Несвицкий с озлобленным видом, красный и на себя не похожий, кричал Кутузову, что ежели он не уедет сейчас, он будет взят в плен наверное. Кутузов стоял на том же месте и, не отвечая, доставал платок. Из щеки его текла кровь. Князь Андрей протеснился до него.
– Вы ранены? – спросил он, едва удерживая дрожание нижней челюсти.
– Раны не здесь, а вот где! – сказал Кутузов, прижимая платок к раненой щеке и указывая на бегущих. – Остановите их! – крикнул он и в то же время, вероятно убедясь, что невозможно было их остановить, ударил лошадь и поехал вправо.
Вновь нахлынувшая толпа бегущих захватила его с собой и повлекла назад.
Войска бежали такой густой толпой, что, раз попавши в середину толпы, трудно было из нее выбраться. Кто кричал: «Пошел! что замешкался?» Кто тут же, оборачиваясь, стрелял в воздух; кто бил лошадь, на которой ехал сам Кутузов. С величайшим усилием выбравшись из потока толпы влево, Кутузов со свитой, уменьшенной более чем вдвое, поехал на звуки близких орудийных выстрелов. Выбравшись из толпы бегущих, князь Андрей, стараясь не отставать от Кутузова, увидал на спуске горы, в дыму, еще стрелявшую русскую батарею и подбегающих к ней французов. Повыше стояла русская пехота, не двигаясь ни вперед на помощь батарее, ни назад по одному направлению с бегущими. Генерал верхом отделился от этой пехоты и подъехал к Кутузову. Из свиты Кутузова осталось только четыре человека. Все были бледны и молча переглядывались.
– Остановите этих мерзавцев! – задыхаясь, проговорил Кутузов полковому командиру, указывая на бегущих; но в то же мгновение, как будто в наказание за эти слова, как рой птичек, со свистом пролетели пули по полку и свите Кутузова.
Французы атаковали батарею и, увидав Кутузова, выстрелили по нем. С этим залпом полковой командир схватился за ногу; упало несколько солдат, и подпрапорщик, стоявший с знаменем, выпустил его из рук; знамя зашаталось и упало, задержавшись на ружьях соседних солдат.
Солдаты без команды стали стрелять.
– Ооох! – с выражением отчаяния промычал Кутузов и оглянулся. – Болконский, – прошептал он дрожащим от сознания своего старческого бессилия голосом. – Болконский, – прошептал он, указывая на расстроенный батальон и на неприятеля, – что ж это?
Но прежде чем он договорил эти слова, князь Андрей, чувствуя слезы стыда и злобы, подступавшие ему к горлу, уже соскакивал с лошади и бежал к знамени.
– Ребята, вперед! – крикнул он детски пронзительно.
«Вот оно!» думал князь Андрей, схватив древко знамени и с наслаждением слыша свист пуль, очевидно, направленных именно против него. Несколько солдат упало.
– Ура! – закричал князь Андрей, едва удерживая в руках тяжелое знамя, и побежал вперед с несомненной уверенностью, что весь батальон побежит за ним.
Действительно, он пробежал один только несколько шагов. Тронулся один, другой солдат, и весь батальон с криком «ура!» побежал вперед и обогнал его. Унтер офицер батальона, подбежав, взял колебавшееся от тяжести в руках князя Андрея знамя, но тотчас же был убит. Князь Андрей опять схватил знамя и, волоча его за древко, бежал с батальоном. Впереди себя он видел наших артиллеристов, из которых одни дрались, другие бросали пушки и бежали к нему навстречу; он видел и французских пехотных солдат, которые хватали артиллерийских лошадей и поворачивали пушки. Князь Андрей с батальоном уже был в 20 ти шагах от орудий. Он слышал над собою неперестававший свист пуль, и беспрестанно справа и слева от него охали и падали солдаты. Но он не смотрел на них; он вглядывался только в то, что происходило впереди его – на батарее. Он ясно видел уже одну фигуру рыжего артиллериста с сбитым на бок кивером, тянущего с одной стороны банник, тогда как французский солдат тянул банник к себе за другую сторону. Князь Андрей видел уже ясно растерянное и вместе озлобленное выражение лиц этих двух людей, видимо, не понимавших того, что они делали.