【问题标题】:Is it possible to ignore [[nodiscard]] in a special case?在特殊情况下是否可以忽略 [[nodiscard]] ?
【发布时间】:2018-04-16 16:34:41
【问题描述】:

C++17 有一个新属性,[[nodiscard]]

假设我有一个Result 结构,它有这个属性:

struct [[nodiscard]] Result {
};

现在,如果我调用返回 Result 的函数,如果我不检查返回的 Result,则会收到警告:

Result someFunction();

int main() {
    someFunction(); // warning here, as I don't check someFunction's return value
}

这个程序生成:

警告:忽略用“nodiscard”声明的函数的返回值 属性 [-Wunused-result]

到目前为止,一切都很好。现在假设,我有一个特殊的功能,我仍然想返回Result,但如果省略检查,我不希望生成此警告:

Result someNonCriticalFunction();

int main() {
    someNonCriticalFunction(); // I don't want to generate a warning here
}

这是因为,someNonCriticalFunction() 做了一些非关键的事情(例如,像printf 这样的事情——我敢打赌,没有人会一直检查printf 的返回值);大多数情况下,我不在乎它是否失败。但我仍然希望它返回Result,因为在极少数情况下,我确实需要它的Result

有可能以某种方式做到这一点吗?


我不喜欢的可能解决方案:

  • 我不喜欢叫它(void)someNonCriticalFunction(),因为这个函数被调用了很多次,很别扭
  • 围绕someNonCriticalFunction() 创建一个包装器,它调用(void)someNonCriticalFunction():我不想仅仅因为这个而有一个不同命名的函数
  • 从结果中删除[[nodiscard]],并将其添加到每个返回Result的函数中

【问题讨论】:

  • 您的要求似乎相互矛盾。你想添加一个警告,这样你就永远不会忽略一个结果,但你想忽略它?
  • 如果它是 特殊 情况,那么它应该不会发生太多,并且使用转换为 void 是可以接受的(带有说明原因的注释)。如果它确实经常发生,那么它就不再是特殊了,我建议您花一些时间考虑一下您的设计和要求。
  • Rust 通过返回 Result<()>(返回空结构的结果类型)来实现这一点。然后,您只需调用 .unwrap() 即可忽略该错误。也许你可以在你的Result 类中添加一个ignore 函数。
  • 你已经排除了所有合理的解决方案。
  • 此外,它还增加了 maintianabilityreadabilitydiscoverability 以使函数 IMO 具有 [[nodiscard]]。它确实让处理“特殊情况”变得非常更容易。

标签: c++ c++17 nodiscard


【解决方案1】:

为什么不使用 <tuple> 标头中的 std::ignore - 这会使丢弃显式:

[[nodiscard]] int MyFunction() { return 42; }

int main() 
{
    std::ignore = MyFunction();
    return 0;
}

【讨论】:

  • 我真的很喜欢这个解决方案。它写起来很快,而且很明显它在做什么。另外,当您想正确解决问题时,很容易搜索到!
  • OP 说他不想使用(void)MyFunction(),因为它被调用了很多而且它“尴尬”,但是在所有调用前加上std::ignore =(void) 更多的语法,并添加 <tuple> 依赖项。我觉得他只能忍气吞声了。 Result 结构很烦人,有时这就是生活。
  • @BillyDonahue 当然 OP 将不得不接受它。鉴于此,他们可能会记得编写富有表现力和明确意图的代码的必要性。 (void)MyFunction(); 确实表达意图,并且在审核期间可能会因其影响而被忽视。 std::ignore = MyFunction(); 不会受这些影响。
  • @sehe (void) 是 C 兼容的、众所周知的、惯用的,并且更通用。它可以应用于可能已经是void 表达式的表达式。 std::ignore 有一个与元组相关的用途,当它用于非设计用途时,其意图更加模糊,而不是更少。
  • 原始指针、错误代码和许多已经灭绝的习语也是如此。如果您要争辩说英语单词没有表达力,那就继续吧,但我说的是您的影响力。 Std::ignore 具有表达意图的好处。这几乎很有趣,因为“忽略”是一个也有目的的词。我想说你可以用它来表达它的意思。
【解决方案2】:

他们说计算机科学中的每个问题都可以通过添加另一层间接来解决:

template <bool nodiscard=true>
struct Result;

template <>
struct Result<false> {
    // the actual implementation
};

template <>
struct [[nodiscard]] Result<true>
    : Result<false>
{
    using Result<false>::Result;
};

这有效地使Result有条件地[[nodiscard]],它允许:

Result<true> someFunction();
Result<false> someNonCriticalFunction();

int main() {
    someFunction();            // warning here
    someNonCriticalFunction(); // no warning here
}

虽然真的,这与:

  • 从 Result 中删除 [[nodiscard]],并将其添加到每个返回 Result 的函数中

这让我开始投票。

【讨论】:

  • 现在他必须访问他返回Result的所有函数,并将它们更改为Result&lt;true&gt;。但如果他要这样做,他也可以将[[nodiscard]] 添加到函数中(这确实是他应该做的,只是他任意排除了该解决方案)。
  • @BenjaminLindley 哦,是的,我同意这一点。查看我的编辑,我在发布后立即输入,然后就再也没有点击 :-)
【解决方案3】:

我推荐你排除的选项:

"从Result 中删除[[nodiscard]],并将其添加到每个返回结果的函数中。"

但由于您似乎对此不满意,这里有另一个解决方案,使用沼泽标准继承:

struct [[nodiscard]] Result {
};

struct DiscardableResult: public Result {
};

对于可以丢弃结果的函数,使用DiscardableResult作为返回类型:

Result func1();
DiscardableResult func2();

func1(); // will warn
func2(); // will not warn

【讨论】:

  • 我刚刚问过这个问题,因为似乎[[nodiscard]] 是粘性的,所以 DiscardableResult 获得了[[nodiscard]] 属性(我已经用 gcc 检查过)。但这可能是一个很好的解决方案,如果 [[nodiscard]] 不会被继承。 stackoverflow.com/questions/47104799/…
  • 顺便说一句,您为什么建议为每个功能添加[[nodiscard]]?对我来说,[[nodiscard]] 属于Result,因为它总是存储错误条件,所以总是必须检查它。我有数百个函数返回Result。标记所有这些,然后维护它们是乏味的。标记Result 要简单得多。
  • @geza: "顺便说一句,你为什么建议为每个函数添加 [[nodiscard]]?" 如果丢弃类型是合理的,那么类型应该不是[[nodiscard]]。这就是将[[nodiscard]] 应用于类型的含义:类型本身的本质意味着您应该永远丢弃它。
  • @geza: “因为它总是存储错误条件,所以总是必须检查” -- 你的问题与此相矛盾。
  • @geza:你问的是如何不必检查那些据说“必须始终检查”的东西。这是一个矛盾。
【解决方案4】:

将结果转换为 (void *)。

int main()
{
    (void *)someFunction(); //Warning will be gone.
}

就编译器而言,这样你“使用”了你的结果。非常适合当您使用已使用 nodiscard 的库并且您真的不想知道结果时。

【讨论】:

  • 很好,但你可能错过了原始海报的这条评论:我不想把它称为 (void)someNonCriticalFunction(),因为这个函数被调用了很多次,很尴尬
  • /s/void */void/ 正确的成语。
猜你喜欢
  • 1970-01-01
  • 2017-04-07
  • 1970-01-01
  • 1970-01-01
  • 2019-06-21
  • 2010-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多