您会看到几种效果的组合。
- 您的调用
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: 警告:从 'int' 转换为 'unsigned int' 可能会改变结果的符号 [-Wsign-转换]
返回 std::unique_ptr(new T(std::forward(args)...));
所以问题是,为什么你没有得到这个std::make_unique()
执行警告?答案本质上是编译器对其系统头文件的这些警告静音。例如,<memory>
标头的 GCC 版本包含 pragma
#pragma GCC system_header
一旦此编译指示出现在头文件中,编译器就不再报告该头文件中所有内容的警告。从GCC 文档:
声明操作系统和运行时库接口的头文件通常不能用严格符合的 C 语言编写。因此,GCC 对系统头文件中的代码进行特殊处理。在 GCC 处理系统标头时,所有警告,除了由 '<code>#warning' (参见诊断)生成的警告外,都会被抑制。
有关更多详细信息,另请参阅
此 SO 帖子。据推测,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)
}
在线尝试。