Is it ok to use std::ignore in order to discard a return value of a function to avoid any related compiler warnings?

Is it ok to use std::ignore in order to discard a return value of a function to avoid any related compiler warnings?


10

I know that you can use static_cast<void>, but it just seems too verbose for me, and not reflecting the original intent that I want to discard a return value, not to cast it to anything.

Recently I stumbled upon std::ignore, which can accept a value of any type, the name is clear and readable, and to me it seems fitting.

I know that the initial intent was to use std::ignore alongside with std::tie to discard any unwanted values, but I guess the original intent of static_cast was to actually cast values for some better reasons than discarding values so the compiler won’t complain.

So, is it OK to use std::ignore for the purpose I described in the question?

For example:

std::ignore = std::transform(...);

12

  • As far as I know std::ignore is only defined in terms of its use in std::tie. Strictly speaking, I think it would have to be std::tie(std::ignore) = std::tie(std::transform(...));.

    – François Andrieux

    14 hours ago


  • 2

    Your usage looks valid. @FrançoisAndrieux see here – the first example is not related to std::tie (std::ignore = dontIgnoreMe();).

    – wohlstad

    14 hours ago


  • 3

    Regarding using static_cast<void>, this is one case where it is generally harmless to use an explicit conversion to void like (void)my_function();. Nobody can use the result, by definition, which eliminates most of the risks related to using them. And it is a relatively well known convention, so shouldn't act as a red flag or cause for confusion or concern.

    – François Andrieux

    14 hours ago


  • 2

    BTW, std::transform doesn't return a [[nodiscard]]. so std::ignore = can even be removed :-), no warnings expected (else we would prefix any expression by it as std::ignore = std::cout << "hello" 🙂 ).

    – Jarod42

    14 hours ago

  • 1

    // my_lint_tool_ignore_flag might be an alternative too. (or maybe the warning is more subtle that just ignoring the return value).

    – Jarod42

    13 hours ago

2 Answers
2


13

Yes, it is okay. In fact, it is considered much better style by some people.

Never cast to (void) to ignore a [[nodiscard]] return value. If you deliberately want to discard such a result, first think hard about whether that is really a good idea (there is usually a good reason the author of the function or of the return type used [[nodiscard]] in the first place). If you still think it’s appropriate and your code reviewer agrees, use std::ignore = to turn off the warning which is simple, portable, and easy to grep.

CppCoreGuidelines ES.48: Avoid casts

An argument against it is that std::ignore is only defined in terms of its effect in std::tie:

template<class... TTypes>
constexpr tuple<TTypes&...> tie(TTypes&... t) noexcept;

Returns: tuple<TTypes&...>(t...). When an argument in t is ignore, assigning any value to the corresponding tuple element has no effect.

[utilities] std::tie

This is mostly a philosophical issue though. std::ignore is implemented in such a way that you can do std::ignore = ... on its own in every major standard library, and may soon be well-defined. See P2968: Make std::ignore a first-class object.

In the end, it’s stylistic preference. You can use (void), static_cast<void>, or std::ignore. They are all acceptable, and which one to use is a matter of opinion.
What matters is that you use a consistent style throughout your project, i.e. if you use std::ignore to discard results in one place, use it everywhere.

3

  • "Never cast to (void) to ignore a [[nodiscard]] return value." Erm, why? Because God said so? There's no rationale given. Ignore that advice.

    – user541686

    4 hours ago


  • @user541686 The point of "nodiscard" is that the caller is supposed to check the result. Not checking the result is normally a bug, hence the compiler warning. Any mechanism to tell future maintainers that you did intend that (and best practise, comments explaining why) would do, and casting to void has 40 years of history for doing this; but people may prefer the newer method. It's only about setting a consistent house style at that point.

    – Graham

    3 hours ago


  • @Graham: "The point of nodiscard is that the caller is supposed to check the result." Yes… we all understand that. That obviously wasn't the point of my comment. "Casting to void has 40 years of history for doing this" which is closer to the point I was making. "But people may prefer the newer method" … but why the newer method?! was the question. The page gives absolutely no reason whatsoever. Which makes for terrible advice; just toss it into the firepit.

    – user541686

    2 hours ago



2

In theory, std::ignore‘s operator= could be implemented as:

[[nodiscard]] auto& operator=(auto&&...) const { return *this; }

So instead of suppressing compiler warnings, assigning to std::ignore could cause a warning.

And std::tuple‘s operator= could do something like this, to suppress the warning when std::ignore is part of a tuple:

(static_cast<void>(std::get<I>(*this) = std::get<I>(rhs)), ...);

In practice, no implementation do this.

Whether it is OK to use std::ignore = depends on whether you want to rely on the fact that no implementation uses [[nodiscard]] in the definition of std::ignore. I, personally, would rather not rely on this undocumented fact.



Leave a Reply

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