Я только что нашел ошибку в своем коде, и я довольно озадачен, что это может произойти, так как это простое несоответствие со знаком и без знака, которое не должно происходить вообще, потому что я компилирую с предупреждением уровня 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)
Вы видите комбинацию нескольких эффектов.
main()
совершенно законно, потому что make_unique
make_unique
не генерирует предупреждение, потому чтоmake_unique
,Более подробно:
Типичная реализация 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)
шаблон
создан для подписанный целое число. Следовательно, вы не получите предупреждение в вашем
код, потому что не происходит беззнаковое / подписанное преобразование.
тем не мение, вы мог по праву ожидать предупреждения от 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&& …) [с 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
компилятор (и, как вы написали в своем комментарии, заголовок временно уменьшает
уровень предупреждения).
В случае 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)
}
Других решений пока нет …