I just found a bug in my code and I am pretty baffled that it could happen, since it is a simple signed/unsigned mismatch - which should not happen at all, because I am compiling with warning level 4, warnings as errors. So I tried to reproduce it, which is pretty simple:
#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;
}
Now I'm asking myself: What's the reason of this? Is it a bug in VS, or is it a general shortcoming of std::make_unique? Is there any way to fix it? (Visual Studio 2015 Community Update 3)
You are seeing a combination of several effects.
main()
is perfectly legitimate because the make_unique
template instantiation matches the signed data type you feed it.make_unique
does not generate a warning because
warnings are typically disabled inside system headers.make_unique
.In more detail:
A typical implementation of std::make_unique
looks like this (compare
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)...));
}
In your case, as you call std::make_unique<MyClass>(-1)
, the template is
instantiated for a signed integer. Hence, you do not get a warning in your
code because no unsigned/signed conversion happens.
However, you could rightfully expect a warning from the make_unique
implementation. After all, when new T(...)
is called with your signed
parameter, a signed/unsigned conversion still happens. As a case in point, take
the following program:
#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;
}
When I compile this using GCC with -Wsign-conversion
, I get the warning
test.cpp: In instantiation of 'std::unique_ptr<_Tp> custom_make_unique(Args&& ...) [with T = MyClass; Args = {int}]':
test.cpp:17:48: required from here
test.cpp:12:63: warning: conversion to 'unsigned int' from 'int' may change the sign of the result [-Wsign-conversion]
return std::unique_ptr(new T(std::forward(args)...));
So the question is, why do you not get this warning for the std::make_unique()
implementation? The answer is essentially that the compiler silences these
warnings for its system headers. For example, the GCC version of the <memory>
header contains the pragma
#pragma GCC system_header
As soon as this pragma is present in a header file, the compiler no longer reports the warnings for everything inside that header. From the GCC documentation:
The header files declaring interfaces to the operating system and runtime libraries often cannot be written in strictly conforming C. Therefore, GCC gives code found in system headers special treatment. All warnings, other than those generated by ā
#warning
ā (see Diagnostics), are suppressed while GCC is processing a system header.
See also this SO post for more details. Presumably a similar approach is taken by Visual Studio's compiler (and as you wrote in your comment, the header temporarily reduces the warning level).
In the case of VisualStudio, there is also something else at work. Note how the
GCC warning above says that there may be a sign conversion problem (depending
on what values users will later feed into custom_make_unique
). It appears
that VisualStudio can only warn if there is a definite sign conversion issue.
See the following program:
#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)
}