生成有关使用复制构造函数和赋值的警告

Generate warning on use of copy constructor and assignment

有没有办法让编译器(这里特别是 MSVC 2017,但其他的可能也很有趣)在特定 class 的复制构造函数和复制- 使用了赋值运算符(并且可以在每个调用点显式抑制,即使是间接调用)?

This question 询问是否产生编译错误,现在使用 C++11 删除的方法很容易,但我希望代码仍然可以编译,只是输出警告。

原因是我有一个 class 目前在整个代码库中被大量复制。我不想阻止它被复制(其中一些是必要的)但我确实想检查每个位置以确定它是否应该更改为移动或通过引用传递。

让编译器暂时将构造函数的使用标记为警告似乎是一个很好的方法。

我试着添加这样的东西:

__declspec(deprecated) MyType(MyType const&) = default;

但这行不通;显然 = default 胜过任何其他修饰符。

我做了同样的事情,但完全实现了这个方法,这个 几乎 有效——它在每个调用站点生成 C4996,我可以查看这些并更改它们或添加:

#pragma warning(suppress:4996)

如果我很高兴这是必需的副本。 (我最终计划删除这些,以及对构造函数的弃用——这只是用来跟踪哪些我还没有处理的内务处理。)

不幸的是,有些情况我无法通过这种方式抑制,例如:

std::vector<MyType> list;
list.push_back(type);
list.emplace_back(MyType{ type });

这些行中的每一行都会引发警告(第一行是因为它是 class 中带有常规复制构造函数的字段声明),但这里只能直接抑制第三行。前两个在 <vector> 内引发警告,并且似乎不受此代码行警告抑制(或禁用)的影响。

有什么方法可以解决这个问题,或者有其他方法可以做我想做的事吗?

它看起来并不完全优雅,但我能够使用以下策略抑制最嘈杂的情况(头文件中 class 的矢量成员)。剩下的案子之后就比较突出了,不需要单独压制;它们可以被修复或忽略。

  1. 添加已弃用的复制和未弃用的移动构造函数和赋值:

    __declspec(deprecated) MyType(MyType const& o) { /* actual impl */ }
    __declspec(deprecated) MyType& operator=(MyType const& o) { /* impl */ return *this; }
    MyType(MyType&&) = default;
    MyType& operator=(MyType&&) = default;
    
  2. 同时添加占位符非弃用转发类型:

    struct SuppressMyType : MyType
    {
        using MyType::MyType;
    #pragma warning(disable:4996)
        SuppressMyType(SuppressMyType const& o) : MyType(o) {}
        SuppressMyType& operator=(SuppressMyType const& o)
            { MyType::operator=(static_cast<MyType const&>(o)); return *this; }
        SuppressMyType(MyType const& o) : MyType(o) {}
        operator MyType() const { return *this; }
    #pragma warning(default:4996)
    };
    
  3. 更改副本使用 SuppressMyType 而不是 MyType 的位置。

  4. 修复其他地方,直到警告消失。

  5. 将所有SuppressMyType替换回MyType,然后删除#1中添加的代码。

有点费解,但它成功了。

换一种方式再考虑一下,换一种方式可能更好(也更容易):

  1. 批量替换所有 MyTypeWarnMyType(当然 MyType 的实际定义除外)。
  2. 使用已弃用的构造函数添加 WarnMyType

    struct WarnMyType : MyType
    {
        using MyType::MyType;
        __declspec(deprecated) WarnMyType(WarnMyType const& o) : MyType(o) {}
        __declspec(deprecated) WarnMyType& operator=(WarnMyType const& o)
            { MyType::operator=(static_cast<MyType const&>(o)); return *this; }
        WarnMyType(WarnMyType&&) = default;
        WarnMyType& operator=(WarnMyType&&) = default;
    };
    
  3. 在检查时逐渐将 WarnMyType 的用途改回 MyType

  4. 移除WarnMyType.
  5. WarnMyType 的任何剩余用途大量替换回 MyType(因为 #3 只会找到执行复制的那些)。

如果这些工具能够像查找命名方法一样容易找到 constructor/operator 用法,那就太好了...