Visual Studio 2015:std::make_unique 中没有 signed/unsigned 不匹配警告?

Visual Studio 2015: No signed/unsigned mismatch warning in std::make_unique?

我刚刚在我的代码中发现了一个错误,我很困惑它会发生,因为它是一个简单的 signed/unsigned 不匹配 - 这根本不应该发生,因为我正在编译警告级别 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的bug,还是std::make_unique的普遍缺点?有什么办法可以解决吗? (Visual Studio 2015 年社区更新 3)

您看到的是多种效果的组合。

  1. main() 中的调用是完全合法的,因为 make_unique 模板实例化与您提供给它的签名数据类型相匹配。
  2. make_unique 的执行不会产生警告,因为 警告通常在系统 headers.
  3. 中被禁用
  4. Visual Studio 似乎无法检测到 潜力 (但不是 definite) make_unique.
  5. 中的符号转换问题

更详细:

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) 时,模板是 实例化为 有符号 整数。因此,您不会在您的 代码,因为没有 unsigned/signed 转换发生。

2。系统 header 通常会禁用警告。

但是,您可以理所当然地期待来自make_unique的警告 执行。毕竟,当 new T(...) 被你的签名调用时 参数,仍然会发生 signed/unsigned 转换。作为一个恰当的例子,拿 以下程序:

#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;
}

当我使用带有 -Wsign-conversion 的 GCC 编译它时,我收到警告

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)...));

所以问题是,为什么您没有收到 std::make_unique() 的警告 执行?答案本质上是编译器将这些静音 系统警告 headers。例如,<memory> 的 GCC 版本 header 包含 pragma

#pragma GCC system_header

一旦此 pragma 出现在 header 文件中,编译器就不再 报告 header 中所有内容的警告。从 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.

另请参阅 this SO post 为 更多细节。 Visual Studio 大概采用了类似的方法 编译器(正如您在评论中所写, header 暂时减少了 警告级别)。

3。看起来您遇到了 VisualStudio 限制。

就 VisualStudio 而言,还有其他因素在起作用。注意如何 上面的 GCC 警告说 可能 是符号转换问题(取决于 用户将 以后 提供给 custom_make_unique 的价值观)。它出现 VisualStudio 只能在存在 definite 符号转换问题时发出警告。 请参阅以下程序:

#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)
}

Try it online.