Attributes are modern ways in C++ to standardize things if their code runs on different compilers. Attributes are used to provide some extra information that is used to enforce conditions (constraints), optimization and do specific code generation if required.
These are like an information manual for the compilers to do some enforcing which will improve performance of the code. Attributes were first seen in C++ 11 and are important parts of the programming language since then, Also with every version some revisions are continuously made to make them more powerful and better.
Let’s see how we can define attributes in C++
For different versions of C++, the syntax for defining the attributes is different.
The syntax for creating attribute in c++ 11 −
The syntax for creating attribute in c++ 17 −
[[using attribute-namespace : attribute_list]]
The syntax for creating attribute in c++ 20 (will be published soon) −
[[contract-attriubute-token contract-level-identifier : expression]]
You can use many of the attributes with variables, functions, classes.
Now, since we know what are attributes, how they are functioning, and how they are defined. Let’s see different standard attributes that are available in c++.
noreturn − This attribute is used to tell the compiler that the function does not return any value.
[[noreturn]] void f();
This function will not return any value, not even void.
The noreturn attribute, when used in c++, enables the compiler to return a warning in case something wrong happens and the flow does not go back to the calling code, like in case of an infinite loop or any error.
carries_dependency − This is used to define all the dependencies that are in release-consume and allows the compiler to optimize by not going through unnecessary memory consuming instructions.
This is majorly used with the declarations of functions or parameters to declare dependencies.
deprecated − This is used to define a deprecated entity in the code. The usage of this deprecated entity is allowed but it is not recommended to use it.
[[deprecated]] [[deprecated (reason)]]
The reason is a string that gives the reason why the depreciation is done and also provides an alternative for the deprecated entity.
The entities that can be deprecated are class, structure, union, typedef-name, static members, functions, namespace, enumeration.
fallthrough − This is used to indicate to the compiler that the fallthrough from to the next case is intentional, due to this the compiler does not warn the fallthrough.
The fallthrough may only be used with switch as the next case is defined for it.
nodiscard − This is used for a function that will return an enumeration will be called from discarded value expression instead of cast to void call. The compiler will also issue a warning for it.
[[nodiscard]] [[nodiscard (reason)]] (added in C++ 20)
The reason is a string that is used to provide a reason for not discarding the result and this will be included in c++ 20.
maybe_unused − This is used to tell the compiler to suppress or eliminate warning that is displayed in case of unused entities.
The entities that can be declared maybe_unused are class, structure, union, typedef-name, static members, functions, variables, enumeration.
likely, unlikely − These are used to define weather the alternative path is more or less likely that the current execution.
These are generally applied to entities that alter the flow of a program like labels and statements.
no_unique_address − This is used to define a data-member that need not have a specific address. It is generally used in the case of non-static data members defining their memory allocation is not necessary.
This is useful when the compiler needs to assign memory location between a normal and a no_unique_address variable the compiler will prioritize the former one.
Optimize_for_synchronized − This is used to define that the given function’s definition has to be optimized for invocation from synchronized statement.
The function defined as optimize_for_synchronized will avoid serializing synchronized blocks.
expects − It specifies the conditions that are must for the arguments of a function for the function to be executed.
[[expects : condition]]
condition defined a condition that is to be fulfilled for the function to be executed.
These were all the attributes that are defined in C++ from C++ 11 to C++ 20. Now, let's see why these attributes are used in programming i.e. what proposed attributes solve?
To add constraints to the code − In many cases, attributes add meaning to code and make it more valid and reduces extra effort.
Provide some more optimisation information to the compiler − Some attributes like fallthrough, likely, maybe_used provide information to the compiler to do specific optimisations.
Escaping warning and errors − Sometimes the logic of programmer goes against the strict rules by C++. This is the case where some attributes come to play and help users to avoid or suppress the warning that are going to occur.