In contemporary C++ programming, there is a provision for utilizing attributes that enhance code efficiency and minimize the effort required by programmers to create extensive code. Attributes in C++ serve as supplementary conditions integrated into the code, which the compiler must adhere to. These attributes may encompass various conditions or details linked to variables or functions. Despite compilers being proficient at optimization, they still have limitations in certain aspects compared to human programmers and may suggest suboptimal generic code due to a lack of insight into the specific problem at hand.
The latest C++ standard introduces new attributes that provide extra instructions to the compiler without directly modifying the code, aiming to address this problem to some extent.
Syntax:
[[attribute_list]]
The subsequent properties are employed in the C++ code:
1. noreturn attribute
This attribute is applied prior to defining any function, indicating that the said function will not yield any output. Although the void keyword serves a similar purpose by not returning any value, the main contrast lies in the fact that a void function does not produce a return value but control still goes back to the function call. On the other hand, with the noreturn attribute, the control does not return to the calling point after the function execution.
C++ Example:
#include <iostream>
#include <string>
using namespace std;
void fxn1()
{
cout<<"fxn1 is called"<<endl;
}
void fxn2()
{
cout << "fxn2 is called after fxn1";
}
int main()
{
fxn1();
fxn2();
}
Output:
Explanation:
In the example provided, function1 (fxn1) has a return type of void, indicating it does not return any value. However, upon invoking fxn1, the program flow will proceed to function2 (fxn2) as the controller will advance to the location where fxn1 was invoked.
C++ Example 2:
#include <iostream>
#include <string>
using namespace std;
[[noreturn]] void fxn1()
{
cout<<"fxn1 is called"<<endl;
}
void fxn2()
{
cout << "fxn2 is called after fxn1";
}
int main()
{
fxn1();
fxn2();
}
Output:
Explanation:
If the noreturn attribute is applied before the fxn1 function in the provided code snippet, the compiler will generate a warning regarding this attribute.
2. maybe_unused attribute
This attribute is employed to suppress compiler warnings that occur when a variable is declared in the code but not utilized.
C++ Example:
#include <iostream>
#include <string>
using namespace std;
int main()
{
[[maybe_unused]] int a=54;
cout<<"hello world";
}
Output:
Explanation:
In the aforementioned example, an unused variable "a" is present. Despite this, the compiler will not generate any error or warning related to this unused variable as the attribute has been employed before the variable name.
3. nodiscard attribute
This property is applied prior to any function, ensuring that the outcome of the function must not be ignored.
C++ Example:
#include <iostream>
#include <string>
using namespace std;
[[nodiscard]] int f()
{
return 0;
}
int main()
{
int x=1;
x=f();
cout<<x;
return x;
}
Output:
Explanation:
In the previous scenario, we received 0 as the output from function f, which we then assigned to variable x, effectively making use of the returned value and avoiding any potential errors.
C++ Example 2:
#include <iostream>
#include <string>
using namespace std;
[[nodiscard]] int f()
{
return 0;
}
int main()
{
int x=1;
f();
cout<<x;
return x;
}
Output:
Explanation:
In the preceding code snippet, the return value from function f has been neglected. The attribute nodiscard has been employed prior to the function, thus an error will be raised indicating that the variable's value cannot be discarded.
4. likely attribute
This attribute is applied to conditional statements that are more likely to be executed.
C++ Example:
#include<iostream>
using namespace std;
int f(int i)
{
switch (i) {
case 1:
[[fallthrough]];
[[likely]] case 2 : return 1;
}
return 2;
}
int main(){
cout<<fxn(5);
}
Output:
Difference between Standard and Non-standard Attributes
- In standard attributes, the syntax is used as a double big bracket [[...]], whereas, in non-standard attributes, we can write the attributes in a non-standard way, and we use some specific keyword like declspec or attribute.
- In standard attributes, there is no warning or error given by the compiler, and the code becomes very portable. In non-standard attributes, code is also portable, but there might be a warning or error by the compiler.
- Standard attributes are almost present in all the compilers, whereas non-standard attributes are generally present in specific compilers only.
- Standard attributes do not use standard namespace syntax, whereas non-standard attributes use standard namespace syntax.