问题
I have an pure virtual class that has a pure virtual method that should be const
, but unfortunately is not. This interface is in a library, and the class is inherited by several other classes, in separate projects.
I'm trying to make this method const
without breaking compatibility (at least for some time), but I cannot find a way to produce a warning when the non-const method is overloaded.
The following is an example of what I was able to produce so far:
- Stage 0: Before the change. Only the non-const version of
Interface::doSomething()
method exists and it is pure virtual. - Stage 1: During the transition period. Both const and non-const versions of the
Interface::doSomething()
method exist. They both have a default implementation in order to allow both old style and new style implementations (they cannot be pure virtual in this stage, since each inherited class will override only one of them). The const version calls the non-const version in order to ensure compatibility with old implementations, the non-const version asserts, since it should never be called. - Stage 2: Only the non-const version of
Interface::doSomething()
method exists and it is pure virtual.
In Stage 1, I'd expect to be able to produce a warning when a class overrides the non-const version of Interface::doSomething()
, in order to warn the user that they should update their code, so that when I switch to Stage 2 the chance to break other people's code is very low.
Unfortunately I cannot find a way to do that. I tried several combinations of flags, both with GCC and Clang. The only thing I was able to do, was to make the compilation fail (e.g. changing it to final
), but that's not how I want to handle this. Is there a way to produce a warning?
#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';
}
};
#endif
int 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;
}
This is the CMakeLists.txt file to build the example using the 3 definitions of 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)
回答1:
It would be nice to have a warning on using a deprecated interface. However my attempts have failed as have yours. I think that unfortunately attributes were not designed with this in mind. I think an attribute applies to the name of an entity which means that you get the warning only when calling the method by name. But I did not study the standard about this.
So, with sadness in my heart I will steal a conclusion from this answer to a mildly related post:
Tell your users that the function is deprecated and shouldn't be used, then move on.
来源:https://stackoverflow.com/questions/49011990/c-deprecation-warning-when-overriding-a-deprecated-virtual-method