Генерировать предупреждение об использовании конструктора копирования и присваивания

Есть ли способ заставить компилятор (здесь, в частности, MSVC 2017, но могут быть интересны и другие) выдавать предупреждение в тех местах, где используются конструктор копирования определенного класса и оператор присваивания копии (и таким образом, чтобы его можно было явно подавить на каждом сайте вызова, даже если он непрямой)?

Этот вопрос касается создания ошибок компиляции, что теперь легко сделать с удаленными методами С++ 11, но я хочу, чтобы код все еще компилируется, просто выводит предупреждения.

Причина в том, что у меня есть класс, который в настоящее время активно копируется в кодовой базе. Я не хочу препятствовать его копированию (некоторые из них необходимы), но я хочу просмотреть каждое местоположение, чтобы определить, следует ли вместо этого изменить его на перемещение или на передачу по ссылке.

Позволить компилятору временно пометить использование конструктора как предупреждение, казалось отличным способом сделать это.

Я попытался добавить что-то вроде этого:

__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 });

Каждая из этих строк вызывает предупреждения (первая, потому что это объявление поля внутри класса с обычным конструктором копирования), но здесь можно напрямую подавить только третью. Первые два вызывают предупреждения внутри <vector> и, похоже, не подвержены подавлению (или отключению) предупреждений в этой строке кода.

Есть ли способ решить это или какой-то другой способ сделать то, что я хочу?


person Miral    schedule 11.01.2019    source источник
comment
К сожалению, один из этих неподавляемых фрагментов кода находится в заголовочном файле, включенном в большое количество исходных файлов, поэтому эта строка создает очень много шума.   -  person Miral    schedule 11.01.2019
comment
Вы можете использовать protected: или private:.   -  person Eljay    schedule 11.01.2019
comment
Это заставит его выдать ошибку.   -  person Miral    schedule 11.01.2019
comment
Если у вас есть ошибка, вы можете изменить сайт вызова, чтобы использовать метод, который выполняет операцию (например, фабричная функция).   -  person Eljay    schedule 11.01.2019
comment
Это не кажется практичным; Я хочу, чтобы он мог копировать возвращаемые значения и тому подобное.   -  person Miral    schedule 11.01.2019
comment
Помните, что все действия, вызывающие или подавляющие предупреждения, носят временный характер: как только я подавлю или иным образом исправлю все предупреждения, я удалю весь этот код, сохранив только те изменения, которые не были подавлены. Так что чем меньше мне придется изменять код для подавления предупреждения, тем лучше, потому что это временно.   -  person Miral    schedule 11.01.2019
comment
Что плохого в том, чтобы просто установить их как ошибки, исправить код в местах, которые вы хотите изменить, а затем восстановить нормальную работу? Похоже, это будет делать то же самое, что вы пытаетесь сделать сейчас — изменить код, найти проблемы, исправить их.   -  person 1201ProgramAlarm    schedule 11.01.2019
comment
Когда возникают ошибки, иногда остальная часть кода не компилируется. (И он определенно не будет компилировать другие проекты, использующие библиотеку, содержащую код.) Таким образом, он не найдет все вхождения.   -  person Miral    schedule 11.01.2019


Ответы (2)


Это не кажется совершенно элегантным, но я смог подавить наиболее шумный случай (векторный член класса в заголовочном файле), используя следующую тактику. Остальные случаи после этого выделялись довольно хорошо, и их не нужно было подавлять по отдельности; они могут быть просто исправлены или проигнорированы.

  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.

Немного запутанно, но это помогло.

person Miral    schedule 11.01.2019

Подумав об этом еще немного после того, как сделал это по-другому, возможно, было бы лучше (и проще) сделать это наоборот:

  1. Массовая замена всех MyType на WarnMyType (кроме фактического определения 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 найдет только те, где было выполнено копирование).

Было бы неплохо, если бы инструменты упростили поиск использования конструктора/оператора, как и для использования именованного метода...

person Miral    schedule 11.01.2019