2

我刚刚在我的代码中发现了一个错误,我很困惑它可能会发生,因为它是一个简单的有符号/无符号不匹配 - 这根本不应该发生,因为我正在编译警告级别 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 社区更新 3)

4

1 回答 1

1

您会看到几种效果的组合。

  1. 您的调用main()是完全合法的,因为make_unique 模板实例化与您提供给它的签名数据类型相匹配。
  2. 的实现make_unique不会产生警告,因为警告通常在系统头文件中被禁用。
  3. 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)
}

在线尝试。

于 2018-02-21T20:25:31.563 回答