【发布时间】: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_lock 或lock_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]]属性。