C++14: [[deprecated]]

[[deprecated]] is another attribute that is useful to mark something (a function, a method, a variable, a class, etc.) as still valid, but that has been superseded by other newer stuff and that probably will be removed in the future.

In a similar vein to [[nodiscard]], [[deprecated]] can return a message explaining why this entity has been marked as such.

The compiler will show a warning when a deprecated entity is being actually used in our code.

For example, I have this code:

#include <iostream>

void print(const std::string& msg)
{
    std::cout << msg << "\n";
}

int main()
{
    print("Hello world");
}

After that a lot of functions and code started to use my print() function, I realize that a newer version with std::string_view instead of std::string could have better performance and, since I do not want to break any code, I consider having both functions in my system.

So, to discourage the usage of my old function, I mark it as deprecated:

#include <iostream>


void println(std::string_view msg)
{
    std::cout << msg << "\n";
}

[[deprecated("Use println instead")]]
void print(const std::string& msg)
{
    std::cout << msg << "\n";
}

int main()
{
    print("Hello world");
}

But, since I am still using the old version in my main() function, the compiler will return a warning like this one:

main.cpp:17:24: warning: ‘void print(const string&)’ is deprecated: Use println instead [-Wdeprecated-declarations]

That will dissappear when I will replace all the invocation to print() with println().

C++17: [[nodiscard]] attribute

C++17 adds a new attribute called [[nodiscard]] to let the user know that a return value from a function or method should be handled properly or assigned to a value.

For example, look to this code:

int sum(int a, int b)
{
  return a + b;
}

int main()
{
  sum(10, 20);
  return 0;
}

It produces no result or side-effects, but if the programmer forgot assigning the return value to a variable by mistake, the error will not be immediately obvious.

Now, in this scenario:

char* getNewMessage()
{
  char* nm = new char[100];
  strcpy(nm, "Hello world");
  return nm;
}

int main()
{
  getNewMessage();
  return 0;
}

There is a memory leak produced because the returned value was not stored anywhere and there is no way to deallocate its memory.

Marking a function or method with [[nodiscard]], encourages the compiler to show a compilation warning when it is invoked and its return value is simply bypassed.

You can also write an additional message with the [[nodiscard]] attribute. That message will be displayed if a warning is generated.

In my examples, we could mark my functions like this:

#include <cstring>

[[nodiscard]]
int sum(int a, int b)
{
  return a + b;
}

[[nodiscard("Release the memory using delete[]")]]
char* getNewMessage()
{
  char* nm = new char[100];
  strcpy(nm, "Hello world");
  return nm;
}

int main()
{
  sum(10, 20);
  getNewMessage();
  return 0;
}

And in this case, g++ returns the following compilation warnings:

In function 'int main()':
<source>:19:6: warning: ignoring return value of 'int sum(int, int)', declared with attribute 'nodiscard' [-Wunused-result]

<source>:20:16: warning: ignoring return value of 'char* getNewMessage()', declared with attribute 'nodiscard': 'Release the memory using delete[]' [-Wunused-result]

Though using it could add a lot of verbosity to your method declarations, it is a good idea using it because it prevents some errors to occur.

More on [[nodiscard]]: https://en.cppreference.com/w/cpp/language/attributes