Visual Studio 2015: нет подписанного / неподписанного предупреждения о несоответствии в std::make_unique?

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

#include <memory>

class MyClass {
public:
    MyClass( unsigned ) {}
};
int main()
{
    MyClass* rawP = new MyClass(-1);                // issues a warning, as expected
    auto uniqueP = std::make_unique<MyClass>(-1);   // NO WARNING??!

    // silence the compiler
    rawP; 
    uniqueP;

    return 0;
}

Теперь я спрашиваю себя: в чем причина этого? Это ошибка в VS, или это общий недостаток std::make_unique? Есть ли способ это исправить? (Visual Studio 2015 Community Update 3)

1 ответ

Решение

Вы видите комбинацию нескольких эффектов.

  1. Звонок в вашем main() совершенно законно, потому что make_unique Реализация шаблона соответствует подписанному типу данных.
  2. Реализация make_unique не генерирует предупреждение, потому что предупреждения обычно отключаются внутри системных заголовков.
  3. Кажется, Visual Studio не может обнаружить потенциальную (но не определенную) проблему преобразования знаков внутри make_unique,

Более подробно:

1. Создание шаблона на самом деле является законным.

Типичная реализация std::make_unique выглядит так (сравните cppreference):

template <typename T, typename... Args>
inline std::unique_ptr<T> make_unique(Args&&... args)
{
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

В вашем случае, как вы называете std::make_unique<MyClass>(-1) шаблон создается для целого числа со знаком. Следовательно, вы не получите предупреждение в своем коде, потому что не происходит беззнакового / подписанного преобразования.

2. Системные заголовки обычно отключают предупреждения.

Тем не менее, вы могли по праву ожидать предупреждение от make_unique реализация. Ведь когда new T(...) вызывается с вашим параметром со знаком, преобразование со знаком / без знака все еще происходит. В качестве примера рассмотрим следующую программу:

#include <memory>

class MyClass
{
public:
  MyClass(unsigned) { }
};

template <typename T, typename... Args>
inline std::unique_ptr<T> custom_make_unique(Args&&... args)
{
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

int main()
{
  auto uniqueP = custom_make_unique<MyClass>(-1);
  (void) uniqueP;
  return 0;
}

Когда я собираю это с помощью GCC с -Wsign-conversion Я получаю предупреждение

test.cpp: в экземпляре 'std:: unique_ptr <_Tp> custom_make_unique (Args &&...) [with T = MyClass; Args = {int}] ':
test.cpp: 17: 48: требуется отсюда
test.cpp:12:63: предупреждение: преобразование в "unsigned int" из "int" может изменить знак результата [-Wsign-преобразование]
return std:: unique_ptr (new T (std:: forward (args)...));

Итак, вопрос в том, почему вы не получаете это предупреждение для std::make_unique() реализация? Ответ, по сути, заключается в том, что компилятор отключает эти предупреждения для своих системных заголовков. Например, версия GCC <memory> заголовок содержит прагму

#pragma GCC system_header

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

Заголовочные файлы, объявляющие интерфейсы к операционной системе и библиотекам времени выполнения, часто не могут быть записаны в строго соответствующем C. Следовательно, GCC придает коду, найденному в системных заголовках, специальную обработку. Все предупреждения, кроме предупреждений #warning '(см. Диагностика), подавляются, когда GCC обрабатывает системный заголовок.

Смотрите также этот пост для более подробной информации. Предположительно, аналогичный подход используется компилятором Visual Studio (и, как вы написали в своем комментарии, заголовок временно снижает уровень предупреждения).

3. Похоже, вы столкнулись с ограничением VisualStudio.

В случае VisualStudio на работе есть еще кое-что. Обратите внимание, как в приведенном выше предупреждении GCC говорится о том, что может быть проблема с преобразованием знака (в зависимости от того, какие значения будут позже вводить пользователи custom_make_unique). Похоже, что VisualStudio может только предупредить, если есть определенная проблема преобразования знака. Смотрите следующую программу:

#include <iostream>

void f(unsigned) { }

template <typename T>
void g(T val) { f(val); } // GCC issues a warning, VS does NOT

int main()
{
  f(-1); // GCC and VS issue a warning
  g(-1); // no conversion warning here (g<int> instantiated)
}

Попробуйте онлайн.

Другие вопросы по тегам