﻿# Ключ /Wp64 и ошибка с обработкой шаблонов

Занимаясь продвижением анализатора Viva64 \(из состава PVS\-Studio\) мы часто комментируем ключ /Wp64 из Microsoft Visual C\+\+\. Кто не в курсе, напомню, что этот ключ появился в Visual Studio 2003 и предназначался для подготовки миграции приложений на 64\-битные системы\. В Visual Studio 2008 ключ /Wp64 считается устаревшим, поскольку надо уже давно компилировать 64\-битные приложения, а не готовиться к этому\. То есть компиляция в 64\-битном режиме выявляет все те же самые ошибки и недостатки кода, что выявлял ключ /Wp64 при сборке 32\-битного приложения\. Причем при компиляции 64\-битного кода это делается гораздо более полно и точно\.

Но помимо сказанного, у ключа /Wp64 есть еще один недостаток, который путает программистов, которые не знакомы с ним\. Это касается проблемы разбора кода, содержащего некоторые шаблоны\. Рассмотрим пример\.

На просторах интернета, можно [найти](https://devblogs.microsoft.com/cppblog/the-future-of-the-c-language/#4421146) в комментариях к блогу разработчиков Visual C\+\+ следующий пример:

```cpp
vector<size_t> vs; // создаем вектор элементов size_t
vector<unsigned int> vi; // создаем вектор элементов unsigned int
size_t s; // есть переменная типа size_t
unsigned int i; // есть переменная типа unsigned int
vs[0] = s; // не должно быть warning
vs[0] = i; // не должно быть warning
vi[0] = s; // должно быть warning (*0)
vi[0] = i; // не должно быть warning
s = vs[0]; // не должно быть warning
i = vs[0]; // должно быть warning (*1)
s = vi[0]; // не должно быть warning
i = vi[0]; // не должно быть warning (*2)
```

Обращаю внимание, что в 32\-битном режиме типы size\_t и unsigned int должны совпадать\.

Теперь скомпилируем этот код в 32\-битном режиме в Visual C\+\+ 2005 и получим следующие сообщения\. В строке отмеченной \(\*1\) как и должно:

warning C4267: '\=' : conversion from 'size\_t' to 'unsigned int', possible loss of data

Но в строке \(\*2\) выдается также:

warning C4267: '\=' : conversion from 'size\_t' to 'unsigned int', possible loss of data

Хотя никакого сообщения здесь быть не должно\.

И при этом в строке \(\*0\) "забыто" сообщение\.

Если же скомпилировать код в 64\-битном режиме, то на строки, отмеченные как \(\*0\) и \(\*1\) как и должно выдается сообщение:

warning C4267: '\=' : conversion from 'size\_t' to 'unsigned int', possible loss of data

Автор примера Stephan T\. Lavavej говорит о проблемах с реализацией ключа в /Wp64 в шаблонах\. Дело в том, что технически ключ компилятора /Wp64 реализуется через специальное ключевое слово [\_\_w64](https://pvs-studio.ru/ru/blog/terms/0053/), добавляемое к описанию типа:

```cpp
#ifdef _WIN64
  typedef __int64 MySSizet;
#else
  typedef int __w64 MySSizet; // Add __w64 keyword
#endif
```

Однако это ключевое слово не вводит нового типа данных, поэтому шаблонные классы vs и vi из этого кода одинаковы:

```cpp
typedef __w64 unsigned int   size_t;
vector<__w64 unsigned int> vs;
vector<unsigned int> vi;
```

И хотя вроде бы vs и vi имеют разный тип, компилятор считает их не без оснований одинаковыми и выдает неправильную диагностику\.

Что делать? В Microsoft Connect заведена ошибка, но как там написано, править ее не будут\. Во\-первых, не понятно как, а, во\-вторых, это актуально только для ключа /Wp64, который объявлен deprecated и будет удален\.

Разрабатываемый нами анализатор Viva64 \(входящий в состав [PVS\-Studio](https://pvs-studio.ru/ru/pvs-studio/)\), хотя тоже не идеален при работе с шаблонами, в данном случае успешно выдает предупреждения для данного кода, но основываясь на других правилах\. В частности, если не отключено предупреждение V101, то он выдает предупреждение при попытке неявного расширения unsigned до типа size\_t, что может также скрывать ошибку\. Таким образом, анализатор Viva64 выдаст следующее:

```cpp
std::vector<size_t> vs;
std::vector<unsigned int> vi;
size_t s;
unsigned int i;
vs[0] = s;
vs[0] = i; //V101: Implicit assignment
           //type conversion to memsize type.
vi[0] = s; //V103: Implicit type conversion
           //from memsize to 32-bit type.
vi[0] = i;
s = vs[0];
i = vs[0]; //V103: Implicit type conversion
           //from memsize to 32-bit type.
s = vi[0]; //V101: Implicit assignment
           //type conversion to memsize type.
i = vi[0];
```

При этом анализатор в ряде случаев может понять, что некие присваивания безопасны и сократить число ложных предупреждений\. Рассмотрим пример:

```cpp
std::vector<unsigned int> vi;
for (size_t i = 0; i < 10; i++)
  vi[i] = i;
```

Здесь компилятор выдает предупреждение:

warning C4267: '\=' : conversion from 'size\_t' to 'unsigned int', possible loss of data

Анализатор Viva64 в свою очередь учитывает, что значение переменной "i" лежит в диапазоне \[0\.\.10\] и данный код не может привести к ошибке\. В результате никакие диагностические сообщения не выдаются\.