【问题标题】:Why not apply [[nodiscard]] to every constructor?为什么不将 [[nodiscard]] 应用于每个构造函数?
【发布时间】:2021-11-13 09:53:05
【问题描述】:

从 C++20 开始,[[nodiscard]] 可以应用于构造函数。 http://wg21.link/p1771有例子:

struct [[nodiscard]] my_scopeguard { /* ... */ };
struct my_unique {
  my_unique() = default;                                // does not acquire resource
  [[nodiscard]] my_unique(int fd) { /* ... */ }         // acquires resource
  ~my_unique() noexcept { /* ... */ }                   // releases resource, if any
  /* ... */
};
struct [[nodiscard]] error_info { /* ... */ };
error_info enable_missile_safety_mode();
void launch_missiles();
void test_missiles() {
  my_scopeguard();              // warning encouraged
  (void)my_scopeguard(),        // warning not encouraged, cast to void
    launch_missiles();          // comma operator, statement continues
  my_unique(42);                // warning encouraged
  my_unique();                  // warning not encouraged
  enable_missile_safety_mode(); // warning encouraged
  launch_missiles();
}
error_info &foo();
void f() { foo(); }             // warning not encouraged: not a nodiscard call, because neither
                                // the (reference) return type nor the function is declared nodiscard

通常构造函数没有副作用。所以丢弃结果是没有意义的。例如,如下丢弃std::vector 是没有意义的:

std::vector{1,0,1,0,1,1,0,0};

如果std::vector 构造函数是[[nodiscard]] 会很有用,这样上面的代码就会产生警告。

确实具有副作用的显着构造函数是锁构造函数,例如unique_locklock_guard。但是这些也是标记为[[nodiscard]] 的好目标,以避免错过范围,如下所示:

std::lock_guard{Mutex};
InterThreadVariable = value; // ouch, not protected by mutex

如果std::lock_guard 构造函数是[[nodiscard]] 会很有用,这样上面的代码就会产生警告。

当然有return std::lock_guard{Mutex}, InterThreadVariable; 这样的案例。但是很少有[[nodiscard]]守卫,并且像return ((void)std::lock_guard{Mutex}, InterThreadVariable);一样在本地压制他们

那么,在什么情况下构造函数应该被nodiscard?

【问题讨论】:

  • 我有一个小型测试运行器,我将其设置为临时使用,它是一个独立的线程池,当它超出范围时开始并行执行提供的测试。我不希望它仅仅因为我没有为此类的实例命名而产生警告。除此之外,我认为这是非常基于意见的,就像我所做的事情是否伟大一样。问题是,一个类的大多数有效用例不会导致它的实例首先被丢弃,而当它们这样做时,它很可能是有意的。此评论是关于一般类,而不是错误。
  • 可以实例化一个匿名对象,其生命周期受表达式限制,例如 lock_guard。声明它的 ctor nodiscard,为图书馆的用户准备一个惊喜。
  • 无论如何,一个更温和的解决方案是引入一个编译器标志,将所有构造函数设置为 nodiscard。如果您的代码片段是在没有考虑 C++20 的情况下编写的,将会有所帮助。
  • @Kaihaku,关于对象中包含的某些过程的好点,作为可能被丢弃的副作用构造函数的示例。相当罕见,但仍然有效的案例。 @bipll,我添加了对表达式 lock_guard 的评论。
  • @Aziuth,这就是问题的重点,为什么要标记[[nodiscard]] 构造函数,而不是相反,并在特殊情况下引入[[may_discard]] 属性。

标签: c++ c++20 nodiscard


【解决方案1】:

pybind11 库中的一个示例:要为 python 包装一个 C++ 类,您可以:

PYBIND11_MODULE(example, m) {
    py::class_<MyClass>(m, "MyClass");  // <-- discarded.
}

【讨论】:

  • 也许吧。但我认为这会暴露一个没有方法的类,所以无论如何声明[[nodiscard]] 可能是值得的。
  • 是的,但有些类(如标签)没有任何成员或成员方法。或者,也许您不需要将它们暴露给 python 代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-14
  • 1970-01-01
  • 2019-09-14
  • 2018-04-26
  • 2017-04-09
  • 1970-01-01
  • 2016-02-17
相关资源
最近更新 更多