std::p ackaged_task 应该删除带有 const 参数的复制 c'tor

std::packaged_task should have deleted copy c'tor with const parameter

本文关键字:复制 参数 tor const ackaged task std 删除      更新时间:2023-10-16

Link https://cplusplus.github.io/LWG/issue2067 提供了以下讨论:

类模板packaged_task是仅移动类型,具有以下形式的已删除复制操作:packaged_task(packaged_task&) = delete;
packaged_task& operator=(packaged_task&) = delete;
请注意,参数类型是非常量。这对我来说不像是一个错字,这种形式似乎从 N2276 的第一篇提案论文中就存在。在引入默认的特殊成员函数之前,使用复制构造函数的任何一种形式都没有太大区别,但现在它产生了可观察到的差异。德国C++新闻组上的一个问题引起了我的注意,其中提出了为什么以下代码无法在最近的 gcc 上编译的问题:

#include <utility>
#include <future>
#include <iostream>
#include <thread>
int main() {
std::packaged_task<void()> someTask([]{ std::cout << std::this_thread::get_id() << std::endl; });
std::thread someThread(std::move(someTask)); // Error here
// Remainder omitted
}

事实证明,该错误是由某些返回类型的 std::bind 的实例化产生的,该返回类型使用了默认的复制构造函数,这会导致与 [class.copy] p8 的 const 声明冲突。

这个问题的某些方面可能与核心语言有关,但我认为它不仅仅是对程序员的服务,如果库将复制操作的通常形式(即那些具有 const 第一个参数类型的操作(声明为删除packaged_task以防止此类问题。

谁能解释一下标记语句的含义?我不知道缺少的 const qualifer 如何影响编译过程,以及如何在标准中解释这种行为。 将 const 添加到已删除的复制构造函数的参数中有什么意义?

下面是一个玩具示例:

struct problem {
problem()=default;
problem(problem&&)=default;
problem(problem&)=delete;
};
template<class T>
struct bob {
T t;
bob()=default;
bob(bob&&)=default;
bob(bob const&)=default;
};
int main() {
problem p;
problem p2 = std::move(p);
bob<problem> b;
bob<problem> b2 = std::move(b);
}

bob<problem>无法编译,因为bob(bob const&)=default在与problem(problem&)=delete交互时出错。

可以说,当标准确定它无法实现bob(bob const&)时,它"应该"干净地出错,并将=default视为=delete(就像我们有problem(problem const&)=delete一样(,但标准措辞在这种极端情况下不会完美无缺。 角落案例的这个角落会很奇怪和古怪,以至于我不确定将其=default转换为=delete的一般规则是正确的!

如果我们problem(problem const&)=delete(好吧,packaged_task(的修复将比我们为=defaultctor规则所做的任何事情都要干净得多。

现在标准钻研:

首先,很明显,上面bob<problem>的隐式声明的复制构造函数将在[class.ctor]中具有签名bob(bob&)。 我什至不会为此引用标准,因为懒惰。

我们去并显式默认bob(bob const&)copy ctor,它在签名上与隐式声明的签名不同。

有关于显式默认函数的规则,它们与签名的冲突在 11.4.2 中。

在显式默认函数 [dcl.fct.def.default] 11.4.2/2 中

2 允许显式默认函数的类型T1F与隐式声明时的类型不同T2如下所示:

—(2.1(T1T2可能有不同的 ref 限定符;和

—(2.2(如果T2有一个类型为const C&的参数,则对应的参数T1可以是C&类型。

如果T1在任何其他方面与T2不同,则:

—(2.3(如果F是赋值运算符,并且返回类型T1与返回类型不同T2或者T1的参数类型不是引用,则程序格式不正确;

—(2.4( 否则,如果F在其第一个声明中明确默认,则将其定义为已删除;

—(2.5( 否则,程序格式不正确。

默认的T1,其中包含const&&,因此(2.2(不适用。

我的阅读实际上已经抓住了它(2.4(;bob(bob const&)的类型以不允许的方式与隐含声明的bob(bob&)不同;但是第一次声明是default的,所以它应该被deleted。

我正在查看 n4713 草案版本;也许旧版本没有该条款。