为什么C++ 11删除的函数参与重载解析?

Meh*_*dad 86 c++ c++11

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

Nic*_*las 112

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

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

struct onlydouble {
  onlydouble(std::intmax_t) = delete;
  onlydouble(double);
};
Run Code Online (Sandbox Code Playgroud)

如果delete完全删除了该函数,那将使= delete语法等效于此:

struct onlydouble2 {
  onlydouble2(double);
};
Run Code Online (Sandbox Code Playgroud)

你可以这样做:

onlydouble2 val(20);
Run Code Online (Sandbox Code Playgroud)

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

onlydouble val(20);
Run Code Online (Sandbox Code Playgroud)

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

= delete意思是"我禁止这个",而不仅仅是"这不存在".这是一个更强烈的声明.

我问为什么C++标准说=删除意味着"我禁止这个"而不是"这不存在"

这是因为我们不需要特殊的语法来说"这不存在".我们通过简单地不声明所讨论的特定"这个"来隐含地得到这个."我禁止这个"代表了一种没有特殊语法就无法实现的结构.所以我们得到特殊的语法来说"我禁止这个"而不是另一个.

您将通过具有明确的获得唯一的功能"此不存在"语法是防止有人从后宣布其存在.而这仅仅是没有用到需要自己的语法.

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

复制构造函数是一个特殊的成员函数.每个类总是有一个拷贝构造函数.正如他们总是有一个复制赋值运算符,移动构造函数等.

这些功能存在; 问题只是称呼它们是否合法.如果你试图说这= delete意味着它们不存在,那么规范就必须解释它对函数不存在意味着什么.这不是规范处理的概念.

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

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

C++规范无法处理"不存在的函数"的概念.它可以处理过载不匹配.它可以处理过载歧义.但它不知道什么不存在.所以= delete在的更为常用的术语定义为"试图把这种失败",而不是不太有用"假装我从来没有写过这条线."

再次,重新阅读第一部分.你不能用"功能不存在" 来做到这一点.这是为什么以这种方式定义的另一个原因:因为= delete语法的一个主要用例是能够强制用户使用某些参数类型,显式转换等等.基本上,要隐藏隐式类型转换.

你的建议不会这样做.

  • @Mehrdad:这可以追溯到我原来的观点,这就是我发布它的原因:如果`= delete`意味着"这个成员不存在",那么我发布的第一个例子将无法阻止人们传递整数到'onlydouble`的构造函数,因为删除*的'onlydouble`重载将不存在*.它不会参与重载决策,因此它不会阻止您传递整数.这是`= delete`语法的一半:能够说,"你不能隐式地将X传递给这个函数." (6认同)
  • @Mehrdad:根据这个逻辑,为什么你需要`= delete`?毕竟,我们可以通过完全相同的事情来说"不可复制":声明复制构造函数/赋值私有.另外,请注意,声明私有内容并不会使其无法赎回;*内的代码*仍然可以调用它.所以它与`= delete`不一样.不,`= delete`语法允许我们以更加明显和合理的方式做一些非常不方便和难以理解的事情. (3认同)
  • @Mehrdad:因为后者*是可能的*:它被称为"不要声明它".那它就不存在了.至于为什么我们需要语法隐藏过载而不是滥用私有......你真的在问[为什么我们应该有一种方法来明确陈述某些东西,而不是滥用其他一些功能来获得相同的效果](http:// stackoverflow.com/a/9458874/734069)?对于任何阅读代码的人来说,这都是显而易见的.它使代码更易于用户理解,并使编写更容易,同时还解决了变通方法中的问题.我们也不需要*lambdas. (2认同)

Ola*_*che 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);  
};  
Run Code Online (Sandbox Code Playgroud)

- 结束示例 ]
[ 示例:通过使用该类的用户声明的运算符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[]  
Run Code Online (Sandbox Code Playgroud)

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

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  
Run Code Online (Sandbox Code Playgroud)

- 结束例子 ]

  • 从例子中可以看出这个理由很清楚,你不觉得吗? (4认同)