наследование — C ++: предупреждение об устаревании при переопределении устаревшего виртуального метода

У меня есть чистый виртуальный класс, который имеет чистый виртуальный метод, который должен быть const, но, к сожалению, нет. Этот интерфейс находится в библиотеке, а класс наследуется несколькими другими классами в отдельных проектах.

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

Ниже приведен пример того, что я смог сделать до сих пор:

  • Этап 0До изменения. Только неконстантная версия Interface::doSomething() метод существует и он чисто виртуальный.
  • Этап 1В течение переходного периода. И константные, и неконстантные версии Interface::doSomething() метод существует. Они оба имеют реализацию по умолчанию, чтобы разрешить реализации как старого, так и нового стиля (на этом этапе они не могут быть чисто виртуальными, поскольку каждый унаследованный класс будет переопределять только одну из них). Константная версия вызывает неконстантную версию для обеспечения совместимости со старыми реализациями, утверждает неконстантная версия, так как она никогда не должна вызываться.
  • Этап 2: Только неконстантная версия Interface::doSomething() метод существует и он чисто виртуальный.

В Этап 1, Я ожидаю, что смогу выдавать предупреждение, когда класс переопределяет неконстантную версию Interface::doSomething(), чтобы предупредить пользователя, что он должен обновить свой код, чтобы при переключении на Этап 2 вероятность взломать чужой код очень мала.
К сожалению, я не могу найти способ сделать это. Я попробовал несколько комбинаций флагов, как с GCC, так и с Clang. Единственное, что мне удалось сделать, — это сделать сборку неудачной (например, изменить ее на final), но это не то, как я хочу справиться с этим. Есть ли способ сделать предупреждение?

#include <iostream>
#include <cassert>

class Interface
{
public:
virtual ~Interface() = default;

// callDoSomething method:
// - stage 0: non const
// - stage 1-2: const
#if (STAGE == 0)
void callDoSomething() { doSomething(); }
#else
void callDoSomething() const { doSomething(); }
#endif

protected:

// non-const doSomething() method:
// - stage 0: pure virtual
// - stage 1: virtual with assert in default implementation (should never be called)
// - stage 2: removed
#if (STAGE == 0)
virtual void doSomething() = 0;
#elif (STAGE == 1)
[[deprecated("Overload const version instead")]]
virtual void doSomething()
{
assert(false);
}
#endif

// const doSomething() method
// - stage 0: N/A
// - stage 1: virtual with default implementation (calls the non-const overload)
// - stage 2: pure virtual
#if (STAGE == 1)
virtual void doSomething() const
{
std::cout << __PRETTY_FUNCTION__ << '\n';
std::cout << "  calling non const version\n";
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"const_cast<Interface*>(this)->doSomething();
#pragma GCC diagnostic pop
}
#elif (STAGE == 2)
virtual void doSomething() const = 0;
#endif
};// Old style implementation: non-const doSomething()
// Allowed only in stages 0 and 1
#if (STAGE == 0 || STAGE == 1)
class Implementation_old : public Interface
{
public:
virtual ~Implementation_old() = default;

protected:
virtual void doSomething() override
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
};
# endif// Old style implementation: const doSomething()
// Allowed only in stages 1 and 2
#if (STAGE == 1 || STAGE == 2)
class Implementation_new : public Interface
{
public:
virtual ~Implementation_new() = default;

protected:
virtual void doSomething() const override
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
};
#endifint main(int argc, char *argv[])
{
Interface* iface = nullptr;

#if (STAGE == 0 || STAGE == 1)
iface = new Implementation_old;
iface->callDoSomething();
delete iface;
#endif

#if (STAGE == 1)
std::cout << "-------------------\n";
#endif

#if (STAGE == 1 || STAGE == 2)
iface = new Implementation_new;
iface->callDoSomething();
delete iface;
#endif

iface = nullptr;

return 0;
}

Это файл CMakeLists.txt для построения примера с использованием 3 определений STAGE

cmake_minimum_required(VERSION 3.5)
project(test_deprecate_non_const)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

add_executable(main_stage_0 main.cpp)
target_compile_definitions(main_stage_0 PRIVATE STAGE=0)

add_executable(main_stage_1 main.cpp)
target_compile_definitions(main_stage_1 PRIVATE STAGE=1)

add_executable(main_stage_2 main.cpp)
target_compile_definitions(main_stage_2 PRIVATE STAGE=2)

4

Решение

Было бы неплохо иметь предупреждение об использовании устаревшего интерфейса. Однако мои попытки потерпели неудачу, как и ваши. Я думаю, что, к сожалению, атрибуты не были разработаны с учетом этого. Я думаю, что атрибут относится к имени объекта, что означает, что вы получаете предупреждение только при вызове метода по имени. Но я не изучал стандарт об этом.

Итак, с грустью в моем сердце я украду вывод из этот ответ на слегка связанный пост:

Расскажи ваши пользователи, что функция устарела и не должны использоваться, а затем двигаться дальше.

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]