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 ответ
Вы видите комбинацию нескольких эффектов.
- Звонок в вашем
main()
совершенно законно, потому чтоmake_unique
Реализация шаблона соответствует подписанному типу данных. - Реализация
make_unique
не генерирует предупреждение, потому что предупреждения обычно отключаются внутри системных заголовков. - Кажется, 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)
}