Modern C++ Features – Attributes

With the increasing standardization of attributes, we get the opportunity to make our code clearer not only to other humans but also to the compiler and other tools.

Attributes are a standard way to deal with non-standard compiler extensions. This might sound strange at first, but let’s have a look at how some compiler extensions work. Apart from the larger extensions that completely tweak the language, many compilers add the possibility to add some little annotations to functions, variables, classes and so on.

Usually, those annotations are pure additions, i.e. if you remove them, you get valid standard C++. Often they use identifiers that contain double underscores because those are reserved for the implementation, so you can’t get conflicts when you port standard C++ code to another compiler.

One example is the __fastcall annotation in MSVC that tells the compiler to pass the first two sufficiently small arguments in registers on x86 architectures. In GCC, a similar behavior can be achieved by using __attribute__((fastcall)).

This already shows the problem: There was no standardized way to handle this kind of compiler extensions prior to C++11. Different compilers would use different ways to describe those annotations. This makes code non-portable or requires to manage the annotations by using preprocessor macros.

C++11 attributes

With C++11, the way to write these annotations was standardized. Attributes can now be portably written using double square brackets:

[[xyz]] void foo(int i, int j) {
  //...
}

Sadly, this does not yet solve the overall problem: Only a few of the attributes are standardized. Compilers are allowed to use their own attributes, but they have to be placed in compiler specific namespaces. E.G., the fastcall attribute in GCC now can be written in a standard-compliant way as [[gnu::fastcall]], but other compilers are not required to understand this.

Until C++17, other compilers are even allowed to fail compilation on unknown attributes, from C++17 on they are required to ignore them. In fact, it seems that MSVC 2015 still does not support the new attribute syntax at all.

Standardized attributes

Compliance issues aside, there are some attributes that have been standardized. Here are the ones that are most important for maintainability:

  • [[deprecated]], [[deprecated("Because it's old")]]: With these attributes, we can mark functions, variables, classes and so on that should not be used anymore, e.g. because they will be removed in the future. Deprecating things like this can be especially useful if you are in the process of refactoring a piece of software, but simply removing a feature is not possible due to its widespread use.
  • [[fallthrough]]: Put this at the end of a case block in a switch statement to signal that you did not write a break statement there on purpose. Otherwise, static analyzers and code reviewers might warn about the missing break, and this it the way to tell everyone that you know what you are doing.
    switch(something) {
    case SPECIAL_CASE:
      prepareForSpecialCase();
      [[fallthrough]]
    default:
      handleAllCases();
    }
    
  • [[nodiscard]]: This attribute tells the compiler to warn about uses of a function or its return type that would discard the value. Values are discarded if they are not assigned to a variable. This is especially the case if a function with a return value is called as a statement or as the left hand of a comma operator. The warning can be turned off by explicitly casting the value to void.
    struct [[nodiscard]] X {
     int i;
    };
    
    X foo() {
     return {42};
    }
    
    [[nodiscard]] int bar() {
     return 3;
    }
    
    void moo() {
     foo(); //Warning: discarded X
     auto i = (bar(), 55); //Warning: discarded bar()
     (void)bar(); //OK
    }
    
  • [[maybe_unused]] does basically the opposite as [[nodiscard]]: Many compilers already warn about unused values in certain cases. [[maybe_unused]] says that it’s actually OK to not use values marked with this attribute.
  • [[noreturn]] is for function declarations only and tells the compiler that this function will not return. That is, the function will either throw exceptions or call finishing functions like exit or terminate on allexecution paths. While it is mainly useful for optimizations it also adds a bit of documentation.

Conclusion

The implementation, as well as the further standardization of attributes, is still work in progress. Still, what is already there can be useful and we should use it where appropriate.

If you want to see some more detailed information about attributes, check out Bartlomiej Filipek’s blog post!

Previous Post
Next Post

3 Comments



  1. As a minor update, it looks like MS won’t be adding any of the new attribute behaviors for VS2017. According to an MS dev no compiler features were added between Preview 5 and the release candidate; and it doesn’t look like any of the C++ 2017 attribute changes made the cutoff.

    https://www.reddit.com/r/cpp/comments/5dai1i/vs_2017_rc_is_now_available/da30hxd/

    https://blogs.msdn.microsoft.com/vcblog/2016/10/11/c1417-features-and-stl-fixes-in-vs-15-preview-5/

    Reply

  2. The page you linked to states that VS2015 does support attribute syntax. In fact:

    [[deprecated]] int Something() …

    1>d:sandboxtry1try1.cpp(20): error C4996: ‘ABC::Something’: was declared deprecated

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *