deprecated
The [[deprecated]] Attribute
The standard attribute [[deprecated]] indicates that the use of the entity to which the attribute pertains is discouraged, typically in the form of a compiler warning.
Description
The standard [[deprecated]] attribute is used to portably indicate that a particular entity is no longer recommended and to actively discourage its use. Such deprecation typically follows the introduction of alternative constructs that are superior to the original one, providing time for clients to migrate to them asynchronously before the deprecated one is removed in some subsequent release.
An asynchronous process for ongoing improvement of legacy codebases, sometimes referred to as continuous refactoring, often allows time for clients to migrate — on their own respective schedules and time frames — from existing deprecated constructs to newer ones, rather than having every client change in lock step. Allowing clients time to move asynchronously to newer alternatives is often the only viable approach unless (1) the codebase is a closed system, (2) all of the relevant code is governed by a single authority, and (3) the change can be made mechanically.
Although not strictly required, the Standard explicitly encourages1 conforming compilers to produce a diagnostic message in case a program refers to any entity to which the [[deprecated]] attribute pertains. For instance, most popular compilers emit a warning whenever a [[deprecated]] function or object is used:
void f(); [[deprecated]] void g(); int a; [[deprecated]] int b; void h() { f(); g(); // Warning: g is deprecated. a; b; // Warning: b is deprecated. }
The [[deprecated]] attribute can be used portably to decorate other entities: class, struct, union, type alias, variable, data member, function, enumeration, template specialization.2
A programmer can supply a string literal as an argument to the [[deprecated]] attribute — e.g., [[deprecated("message")]] — to inform human users regarding the reason for the deprecation:
[[deprecated("too slow, use algo1 instead")]] void algo0(); void algo1(); void f() { algo0(); // Warning: algo0 is deprecated; too slow, use algo1 instead. algo1(); }
An entity that is initially declared without [[deprecated]] can later be redeclared with the attribute and vice versa:
void f(); void g0() { f(); } // OK, likely no warnings [[deprecated]] void f(); void g1() { f(); } // Warning: f is deprecated. void f(); void g2() { f(); } // Warning: f is deprecated still.
As shown in g2 in the example above, redeclaring an entity that was previously decorated with [[deprecated]] without the attribute leaves the entity still deprecated.
Use Cases
Discouraging use of an obsolete or unsafe entity
Decorating any entity with the [[deprecated]] attribute serves both to indicate a particular feature should not be used in the future and to actively encourage migration of existing uses to a better alternative. Obsolescence, lack of safety, and poor performance are common motivators for deprecation.
As an example of productive deprecation, consider the RandomGenerator class having a static nextRandom member function to generate random numbers:
struct RandomGenerator { static int nextRandom(); // Generate a random value between 0 and 32767 (inclusive). };
Although such a simple random number generator can be useful, it might become unsuitable for heavy use because good pseudorandom number generation requires more state (and the overhead of synchronizing such state for a single static function can be a significant performance bottleneck), while good random number generation requires potentially high overhead access to external sources of entropy. The rand function, inherited from C and available in C++ through the <cstdlib> header, has many of the same issues as our RandomGenerator::nextRandom function, and similarly developers are guided to use the facilities provided in the <random> header since C++11.
One solution is to provide an alternative random number generator that maintains more state, allows users to decide where to store that state (the random number generator objects), and overall offers more flexibility for clients. The downside of such a change is that it comes with a functionally distinct API, requiring that users update their code to move away from the inferior solution:
class StatefulRandomGenerator { // ... (internal state of a quality pseudorandom number generator) public: int nextRandom(); // Generate a quality random value between 0 and 32767, inclusive. };
Any user of the original random number generator can migrate to the new facility with little effort, but that is not a completely trivial operation, and migration will take some time before the original feature is no longer in use. The empathic maintainers of RandomGenerator can decide to use the [[deprecated]] attribute to discourage continued use of RandomGenerator::nextRandom() instead of removing it completely:
struct RandomGenerator { [[deprecated("Use StatefulRandomGenerator class instead.")]] static int nextRandom(); // ... };
By using [[deprecated]] as shown in the previous example, existing clients of RandomGenerator are informed that a superior alternative, BetterRandomGenerator, is available, yet they are granted time to migrate their code to the new solution rather than having their code broken by the removal of the old solution. When clients are notified of the deprecation (thanks to a compiler diagnostic), they can schedule time to rewrite their applications to consume the new interface.
Continuous refactoring is an essential responsibility of a development organization, and deciding when to go back and fix what’s suboptimal instead of writing new code that will please users and contribute more immediately to the bottom line will forever be a source of tension. Allowing disparate development teams to address such improvements in their own respective time frames, perhaps subject to some reasonable overall deadline date, is a proven real-world practical way of ameliorating this tension.
Potential Pitfalls
Interaction with treating warnings as errors
In some code bases, compiler warnings are promoted to errors using compiler flags, such as -Werror for GCC and Clang or /WX for MSVC, to ensure that their builds are warning-clean. For such code bases, use of the [[deprecated]] attribute by their dependencies as part of the API might introduce unexpected compilation failures.
Having the compilation process completely stopped due to use of a deprecated entity defeats the purpose of the attribute because users of such an entity are given no time to adapt their code to use a newer alternative. On GCC and Clang, users can selectively demote deprecation errors back to warnings by using the -Wno-error=deprecated-declarations compiler flag. On MSVC, however, such demotion of warnings is not possible, and the available workarounds, such as entirely disabling the effects of the /WX flag or the deprecation diagnostics using the -wd4996 flag, are often unsuitable.
Furthermore, this interaction between [[deprecated]] and treating warnings as errors makes it impossible for owners of a low-level library to deprecate a function when releasing their code requires that they do not break the ability for any of their higher-level clients to compile; a single client using the to-be-deprecated function in a code base that treats warnings as errors prevents the release of the code that uses the [[deprecated]] attribute. With the frequent advice given in practice to aggressively treat warnings as errors, the use of [[deprecated]] might be completely unfeasible.