94

为什么 C++11 让deleted”函数参与重载决议
为什么这很有用?或者换句话说,为什么它们被隐藏而不是被完全删除?

4

2 回答 2

126

语法的一半目的= delete是能够防止人们使用某些参数调用某些函数。这主要是为了防止某些特定场景下的隐式转换。为了禁止特定的过载,它必须参与过载解决。

您引用的答案为您提供了一个完美的例子:

struct onlydouble {
  onlydouble(std::intmax_t) = delete;
  onlydouble(double);
};

如果delete完全删除该函数,这将使= delete语法等同于:

struct onlydouble2 {
  onlydouble2(double);
};

你可以这样做:

onlydouble2 val(20);

这是合法的 C++。编译器将查看所有构造函数;它们都没有直接采用整数类型。但是其中一个可以在隐式转换后接受它。所以它会这么叫。

onlydouble val(20);

不是合法的 C++。编译器将查看所有构造函数,包括deleted 构造函数。它将看到一个完全匹配的 via std::intmax_t(它将完全匹配任何整数文字)。所以编译器会选择它然后立即发出错误,因为它选择了一个deleted 函数。

= delete意思是“我禁止这个”,而不仅仅是“这不存在”。这是一个更强有力的声明。

我在问为什么 C++ 标准说 = delete 意味着“我禁止这个”而不是“这个不存在”

这是因为我们不需要特殊的语法来说“这不存在”。我们通过简单地不声明所讨论的特定“this”来隐含地得到这一点。“我禁止这个”表示没有特殊语法就无法实现的结构。所以我们得到特殊的语法来说“我禁止这个”而不是其他的东西。

通过明确的“这不存在”语法,您将获得的唯一功能是防止某人稍后声明它存在。这还不足以需要它自己的语法。

否则无法声明复制构造函数不存在,并且它的存在会导致无意义的歧义。

复制构造函数是一个特殊的成员函数。每个类总是有一个复制构造函数。就像他们总是有一个复制赋值运算符、移动构造函数等一样。

这些功能存在;问题只是打电话给他们是否合法。如果您试图说这= delete意味着它们不存在,那么规范将不得不解释函数不存在的含义。这不是规范处理的概念。

如果您尝试调用尚未声明/定义的函数,则编译器将出错。但它会因为未定义的标识符而出错,而不是因为“函数不存在”错误(即使你的编译器以这种方式报告它)。各种构造函数都由重载决议调用,因此它们的“存在”是在这方面处理的。

在每种情况下,要么有一个通过标识符声明的函数,要么是一个构造函数/析构函数(也通过标识符声明,只是一个类型标识符)。运算符重载将标识符隐藏在语法糖后面,但它仍然存在。

C++ 规范无法处理“不存在的函数”的概念。它可以处理过载不匹配。它可以处理过载歧义。但它不知道什么不存在。所以= delete被定义为更有用的“尝试称之为失败”,而不是不太有用的“假装我从未写过这行”。

再次,重新阅读第一部分。你不能用“函数不存在”来做到这一点。这也是它被定义为这种方式的另一个原因:因为= delete语法的主要用例之一是能够强制用户使用某些参数类型、显式转换等等。基本上,为了阻止隐式类型转换。

你的建议不会那样做。

于 2012-12-29T20:37:14.683 回答
10

C++ 工作草案 2012-11-02 并未提供此规则背后的基本原理,仅提供一些示例

8.4.3 删除的定义 [dcl.fct.def.delete]
...
3 [示例:可以使用以下命令强制执行非默认初始化和非整数初始化

struct onlydouble {  
  onlydouble() = delete; // OK, but redundant  
  onlydouble(std::intmax_t) = delete;  
  onlydouble(double);  
};  

结束示例]
[示例:可以通过使用已删除的用户声明的运算符 new 为该类定义来防止在某些新表达式中使用该类。

struct sometype {  
  void *operator new(std::size_t) = delete;  
  void *operator new[](std::size_t) = delete;  
};  
sometype *p = new sometype; // error, deleted class operator new  
sometype *q = new sometype[3]; // error, deleted class operator new[]  

结束示例]
[示例:通过使用复制构造函数和复制赋值运算符的已删除定义,然后提供移动构造函数和移动赋值运算符的默认定义,可以使类不可复制,即只能移动。

struct moveonly {  
  moveonly() = default;  
  moveonly(const moveonly&) = delete;  
  moveonly(moveonly&&) = default;  
  moveonly& operator=(const moveonly&) = delete;  
  moveonly& operator=(moveonly&&) = default;  
  ~moveonly() = default;  
};  
moveonly *p;  
moveonly q(*p); // error, deleted copy constructor  

结束示例]

于 2012-12-29T22:25:46.693 回答